Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

When are Partial Classes comming to java?

Posted on July 29, 2020 By Luis Fernandez

If you write Java and hang around friends who use C sharp, you have probably heard about partial classes and wondered why we do not have the same thing. The pitch is simple. split a class into multiple files, let the compiler stitch them together, and keep generated code away from your hand written bits. For teams that rely on code generation, this sounds like a dream. So the question keeps popping up. when are partial classes coming to Java?

What are partial classes and why people like them

In C sharp you can declare a class in more than one file using the partial keyword. The compiler merges the pieces into one type at compile time. This keeps generated members in one file and your custom members in another file with no manual merge. If a tool re generates code, it will not blow away your edits. That is the pitch that makes .NET folks smile when they talk about partial classes.

// File: Person.Generated.cs
public partial class Person {
    public string FirstName { get; }
    public string LastName { get; }
}

// File: Person.cs
public partial class Person {
    public string FullName() => $"{FirstName} {LastName}";
}

Other ecosystems pull a similar trick in different ways. VB.NET has the same keyword. Objective C has categories to extend a class in separate files. Ruby lets you reopen classes. TypeScript does declaration merging for some shapes like interfaces and namespaces. Not the same exact feature, but the vibe is there. Keep generated and custom code in separate boxes and let the compiler bring it together.

State of Java right now

Java does not have partial classes. The Java Language Specification expects one top level public class per file and the compiler does not merge class bodies from different sources. There is no JEP on partial classes on the roadmaps I have seen, and the noise on OpenJDK lists has been about records, pattern matching for instanceof, and early talk around sealed types. All cool stuff, just not this feature.

If you are asking when partial classes are coming to Java, the honest answer today is not any time soon. There is no proposal on the table. No hint in the toolchains. The language keeps moving, but the design still favors clear and simple mapping from a source file to a type. That is part of its identity.

So how do we do code generation without partial classes

We already ship tons of code with generation in Java, just with different patterns. The workhorse is JSR 269 annotation processing. Tools like AutoValue, Immutables, Dagger, MapStruct, and Moshi generate classes during compilation. Lombok goes further and tweaks the compiler AST to act like members were written in your file. Each approach tries to give you the feel of partial classes, even if the mechanics are not the same.

// AutoValue example
@AutoValue
abstract class Person {
  abstract String firstName();
  abstract String lastName();

  static Person create(String first, String last) {
    return new AutoValue_Person(first, last);
  }

  String fullName() {
    return firstName() + " " + lastName();
  }
}

With AutoValue the generated class AutoValue_Person sits next to your source and you never touch it. Your file stays small and readable. You get type safety, null checks, builders if you want them, and you keep custom logic in your class. This is the most common flavor of code generation in Java today, and it is battle tested in Android and server projects.

Emulating partial classes with clear patterns

One easy pattern is to keep generated code in a class with a predictable name and then delegate from your hand written class. You can wire this up with a static factory and a private field. It reads clean and keeps the generated stuff isolated while letting you add business methods where you write code every day.

// Generated at build time
final class PersonGen {
  final String firstName;
  final String lastName;

  PersonGen(String first, String last) {
    this.firstName = first;
    this.lastName = last;
  }
}

// Hand written wrapper
public final class Person {
  private final PersonGen gen;

  private Person(PersonGen gen) { this.gen = gen; }

  public static Person of(String first, String last) {
    return new Person(new PersonGen(first, last));
  }

  public String firstName() { return gen.firstName; }
  public String lastName() { return gen.lastName; }

  public String fullName() { return gen.firstName + " " + gen.lastName; }
}

Another trick is to use interfaces with default methods as a kind of mixin. The generator emits an interface with accessors and you implement it in your type. You can ship behavior in defaults and still keep the free hand to add more. This is not full partial class magic, but it gets you reusable behavior without inheritance games.

// Generated
interface PersonModel {
  String firstName();
  String lastName();

  default String fullName() {
    return firstName() + " " + lastName();
  }
}

// Hand written
final class Person implements PersonModel {
  private final String first;
  private final String last;

  Person(String first, String last) {
    this.first = first;
    this.last = last;
  }

  public String firstName() { return first; }
  public String lastName() { return last; }

  // Extra behavior lives here
  public boolean isEmpty() { return first.isEmpty() && last.isEmpty(); }
}

If you control the package, the buddy class approach is handy. Put generated code in a package private type and let your public class call into it. The compiler keeps visibility safe, and you still split responsibilities. Your public API stays clean while the generated helper does the heavy lifting.

// package com.example.person

// Generated
final class PersonBuddy {
  static String fullName(String first, String last) {
    return first + " " + last;
  }
}

// Hand written
public final class Person {
  private final String first;
  private final String last;

  public Person(String first, String last) {
    this.first = first;
    this.last = last;
  }

  public String fullName() {
    return PersonBuddy.fullName(first, last);
  }
}

Build setup that does not bite

Good generation depends on good build wiring. In Maven put generated sources under target/generated-sources/annotations. The compiler plugin already adds that path for processors, so your IDE sees the files. If you roll your own generator, configure the build to add the directory and mark it as generated to avoid style checks on it.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>com.google.auto.value</groupId>
        <artifactId>auto-value</artifactId>
        <version>1.7.4</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>

In Gradle the Java plugin already wires the annotation processor path. If you generate files yourself, add a task that writes into $buildDir/generated/sources and hook it into compileJava. Mark the directory as a source set so your IDE indexes it. This keeps generated code visible and keeps the act of generation repeatable.

plugins {
  id 'java'
}

dependencies {
  annotationProcessor 'com.google.auto.value:auto-value:1.7.4'
}

def genDir = "$buildDir/generated/sources/mygen/java/main"

task generateStuff {
  outputs.dir genDir
  doLast {
    // write files into genDir
  }
}

sourceSets.main.java.srcDir genDir
compileJava.dependsOn generateStuff

Lombok and the elephant in the room

Lombok is the closest feel to partial classes inside a single file. The annotations expand into getters, setters, constructors, and more during compilation. You write less code and keep behavior in the same file. The trade is compiler plugin hooks and some extra care in IDE configuration. Many teams are happy with that trade. If you prefer pure annotation processing, AutoValue and Immutables give you a more explicit shape.

Would Java ever add partial classes

Never say never, but there are good reasons Java has avoided it. The language favors a very direct mapping from a file to a type. It makes error messages simple, tooling predictable, and source organization obvious. Partial classes add questions. where do you look for a method. how do you manage ordering and visibility across files. how do you handle incremental compilation if pieces live in different places. None of this is impossible, it just cuts against the grain.

The upside of partial classes is clear for code generation, yet the current ecosystem already solves most pain. With records in preview, a lot of boilerplate for simple data carriers will fade. With better pattern matching, constructors and visitors get nicer. None of these equal partial classes, but the need shrinks in many day to day cases.

Practical checklist for teams today

Pick a generation style and stick to it. If you want minimal magic, go with AutoValue or Immutables. If you want a single file story, pick Lombok. Keep generated code out of version control. mark it as derived in your IDE. Stamp generated files with @Generated and a comment that explains the source. Expose small public APIs and keep the rest package private. The fewer places your code can be extended from, the easier your life becomes.

When you build generators, design for clear ownership boundaries. Your tool owns the generated class. humans own the wrapper or implementation that adds behavior. Avoid mixing sources for the same file. Write unit tests against the public API, not the generated internal types. If you ever need to swap the generator, you will thank your past self.

So, when are partial classes coming to Java? Not this season. The language is moving forward in other areas and the community has patterns that work today. If you miss the feature, the best move is to lean on processors, use composition, and keep your source tree honest. Your build will be faster, your team will know where to look, and your diffs will stay calm when a tool rewrites code on a busy morning.

Creation date: 2020-07-29T09:26:55

Code Development Practices Software Engineering coding-practicesjavaspring

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes