At my org we extensively use @Value.Immutable annotation to generate Immutable classes (usually with builders) for our internal data transfer objects (DTOs). In our codebase I've seen both interfaces and abstract classes being used for such DTO definitions.

For example for a DeviceData DTO we could have an interface as follows

@JsonDeserialize(as = ImmutableDeviceData.class)
public interface DeviceData {

    DeviceIdentifier deviceIdentifier();

    DeviceType deviceType(); // enum

or equivalently we could have abstract class with identical body as above interface

public abstract class DeviceData {

Either ways, we instantiate the DTO as follows

final var myDeviceData = ImmutableDeviceData.builder()

At times we also add certain precondition checks using @Value.Check annotations such as following, which again work identically with both interface and abstract classes

@Value.Immutable(builder = false)
public abstract class DeviceIdentifier {
    public String value();

    default void validate() {
        Validate.notBlank(value(), "DeviceIdentifier cannot be blank");

Additionally there are situations where we have to declare static fields such as regex Pattern in case of EmailAddress DTO; here again Java17 makes it possible in both abstract class and interfaces alike.

Considering this specific use case of Immutable data-transfer objects, are there any pros or cons of preferring abstract class over interface or vice-versa?

  • 3
    The pros and cons for using an abstract class vs an interface in this particular scenario are the same pros and cons you would have in any other scenario. Commented Aug 25, 2023 at 18:24
  • I believe that @RobertHarvey is correct in his assessment. Are you looking for the advantages and disadvantages in general or do you think there's something in particular in this specific case that we aren't seeing.
    – JimmyJames
    Commented Aug 25, 2023 at 20:04
  • I'm starting out writing a new project from scratch and want to understand which of these two would be the better choice given the immutable DTOs use-case. Particularly I'm interested in understanding if there are any pitfalls associated with either of the approaches. Commented Aug 25, 2023 at 21:29
  • 1
    @RobertHarvey I think this particular scenario, there are fewer pros and cons. In this case interface vs class is barely distinguishable.
    – Caleth
    Commented Aug 29, 2023 at 8:30
  • 1
    @justme primary reason we don't want to completely move over to records is that there's limited support for implementing builder-pattern with them [ref-1, ref-2]. We have presently decided to use records for the 'tiny' DTOs such as Coordinate, but for complex ones having many Optionals we are continuing to use abstract class Commented Sep 21, 2023 at 9:02

1 Answer 1


There's not much in it, either way. The consistency you gain from picking one and sticking with it is likely to be more benefit than any potential drawbacks of either option.

That said, there are some minor differences:

  • If any of these are inner classes, you need to remember to make them static (as per the documentation). If it was an inner interface, it would be static implicitly.
  • Abstract methods on classes may have different access modifiers, whereas interface methods are implicitly public. Would you find a package access getter useful (bearing in mind the builder method will still be public), or would you prefer everything to be consistently and implicitly public?
  • The library can be configured to use default interface methods to provide default values for parameters. If using abstract classes, you'd need to use an annotation on the method instead (this is also the default behaviour for interfaces).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.