projectlombok / lombok

Very spicy additions to the Java programming language.
https://projectlombok.org/
Other
12.93k stars 2.4k forks source link

Support for interfaces #1486

Open douglassparker opened 7 years ago

douglassparker commented 7 years ago

Although the issue of interface support has been previously raised, I would like to propose a way to support interfaces without breaking existing code. There are two use cases. An interface-centric approach is appropriate when there are multiple implementations of an interface are desired. If there is only one implementation, a simpler class-centered approach will suffice.
Example interface-centered approach:

@Data
@Entity(…)
public interface Person {
    public static final val FOO = “foo”;
    @Column(…) private val String name;
    @Column(…) private val String email;
    void someOtherMethod();  
}

public class PersonEntity implements Person { 
    public void someOtherMethod() { … }  
}

@ExcludeAnnotations
public class PersonBean implements Person { 
    public void someOtherMethod() { … }
} 

I propose to introduce two new annotations, @ExcludeAnnotations and @IncludeAnnotations. If neither is coded, @IncludeAnnotations is assumed. These annotations determine the generation of non-Lombok annotations. It is only allowed classes that implement a Lombok-annotated interface. Both annotations may include a value attribute that is a list of annotation class names that narrow the list of annotations to be copied to the concrete classes. If the above was coded, it would be equivalent to coding this:

public interface Person {
    // public static final fields stay in the interface
    String FOO = “foo”;
    String getName();
    void setName();
    String getEmail();
    void setEmail(String email);
    default boolean equals(){ /* same as now */ }
    default int hashCode() { /* same as now * / 
    default String toString() { /* same as now * /  
} 

@Data
@Entity
public class PersonEntity implements Person {
    @Column(…) private val String name;
    @Column(…) private val String email;
}

@Data
// No annotations
public class PersonBean implements Person {
    private val String name;
    private val String email;
}  

In more detail, here are the implications for applying Lombok annotations to interfaces.

Care must be taken to remove any unneeded import during code generation.

A simpler approach can be taken when one wishes to start with a class and generate one or more interfaces from the class. We introduce another new annotation @Interface. It has two attributes. The “package” attribute gives a package name. If omitted, it is the same as the package of the implementation class. The “name” attribute (synonym “value”) gives the interface name. If applied at the class level, it applies to all attributes in the class. It can also be applied at the field level. If applied at both levels, the field level takes precedence. A @NoInterface annotation can be used to exclude a member from any interface.

Example:

@Data
@Entity(…)
@Interface(name=“Person”)
public class PersonEntity {

    public static final val FOO = “foo”;

    @Column(…)
    private val String name;

    @Interface(“HasEmail”)
    @Column(…) 
    private val String email;

    public  void someOtherMethod() {…}; 

    @NoInterface 
    public void stillAnotherMethod() {…};   
}

// Generated code
public interface Person {
    String FOO = “foo”; 
    String getName();
    void setName();
    void someOtherMethod(); 
} 
public interface HasEmail {
    String getEmail();
    void setEmail(String email);
} 
public class PersonEntity implements Person, HasEmail {
    // normal code generation except FOO is not included
}

This is just a first cut. For example, suppose we wanted HasEmail to just have the getter and no setter. Or suppose we wanted the Person interface to extend HasEmail. Further refinements would be needed to support this.

All this may seem like a lot of trouble to avoid coding the interfaces manually, but imagine the savings for large classes with dozens of attributes.

rzwitserloot commented 4 years ago

Generating entirely new source files isn't really a lombok thing; these examples are also unclear to me: Lots of stuff just doesn't make sense. Why do your setters have no arguments? Why do you fields have 'val' as a type? Are these typos or intentional? I need a much simpler 'imagine lombok worked as follows: You write ', and it's as if you have '', without oversights such as using 'val' for fields (or if you really meant to write that: I don't think there's any way we can then make heads or tails of what we should generate if you do that).

This doesn't sound like a job for lombok, but I might reconsider if I have a clearer idea of what you want to accomplish.

Start with a simple case before bringing in includes and excludes, if you can.

ZenLiuCN commented 4 years ago

looks really like some kotlin thing. on this condition why not just use kotlin.