Using Lombok #SuperBuilder annotation with toBuilder on an abstract class? - java

I have the following classes:
#SuperBuilder(toBuilder = true)
public abstract class Parent {
//...
}
#SuperBuilder(toBuilder = true)
public class Child extends Parent {
//...
}
#SuperBuilder(toBuilder = true)
public class Child2 extends Parent {
//...
}
Why am I unable to call toBuilder() on an instance of the abstract class (Parent) as shown in the following code?
public copy(Parent parent) {
parent.toBuilder().build();
}

In fact, as Hossein Nasr already explained, Lombok cannot know whether there are subclasses that do not have toBuilder=true.
Lombok could require all direct subclasses of an abstract class to also use toBuilder by adding an abstract toBuilder() method on the abstract class. However, there may be use-cases where subclasses should not have a toBuilder (although I haven't seen any good examples of those). Furthermore, indirect subclasses may still lack the toBuilder feature. Therefore, lombok does not enforce toBuilder on subclasses of abstract classes.
Good news is that you can easily work around it in your case (only direct subclasses) by adding the abstract toBuilder() method to your abstract class Parent manually:
#SuperBuilder(toBuilder = true)
public abstract class Parent {
public abstract ParentBuilder<?, ?> toBuilder();
}
With this change, your copy method compiles and works as expected.
If you also have indirect subclasses, you have to find other means to ensure they also have #SuperBuilder(toBuilder = true). If those would not have this annotation, you may experience strange behavior (e.g. calling copy() will instantiate a different class).
Disclaimer: I implemented the #SuperBuilder feature.

It's probably because Lombok can not guarantees that every child class of Parent is also marked as #SuperBuilder(toBuilder=true) and if so, Lombok can not call the toBuilder of that instance;

Related

Retrieve Class instance of Generic Type without taking the Generic as an input

I have a class:
public abstract class BaseDaoImpl<T extends BaseModel> implements BaseDao<T> {
}
I'm also using annotations to generate SQL queries (for various reasons I'm not able to use a framework such as hibernate) such as #Table & #Column
I would like to be able to retrieve the <T extends BaseModel> .class instance without having to take T as an input on a method.
I suppose the easy alternative would be to create a method:
public void set(Class<T> clazz){}
However I'd like to avoid this if possible to keep my code as streamlined as possible.
Is this possible?
Although using reflection is a bit of a code smell, you can get the information you need:
Class<T> modelImplementationClass = (Class<T>)
((BaseModel)this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
Reflection is a Java API that allows you to access or modify the behavior of methods, classes, interfaces at runtime.
Reflection should generally be avoided as it's quite slow and breaks abstraction by revealing internal implementation details.
Unfortunately, it's not possible to do due to type erasure: you have to force your classes to provide meta-information in runtime. I would do something like this (an adapted snippet from a big real project).
public abstract class AbstractBaseDao<T extends BaseModel> implements BaseDao<T>{
public abstract Class<T> getType();
}
class ConcreteModel extends BaseModel {/*...*/}
class ConcreteDao extends AbstractBaseDao<ConcreteModel> {
#Override
public Class<ConcreteModel> getType() {
return ConcreteModel.class;
}
}
An alternative way is to define a custom annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Type {
Class value();
}
interface BaseDao<T extends BaseModel> { }
#Type(ConcreteModel.class)
public class ConcreteDao implements BaseDao<ConcreteModel> { }
...and use it in some processor for your DAOs, but this will require some additional infrastructure code to register all annotated classes. And - again - you cannot limit type bounds within annotations.

Lombok builder inheritance with Complex Class Structure

I've read other questions regarding lombok's builder and inheritance but none of the solutions have worked. Using Lombok version 1.18.4 and Java 11.
I'm trying to inherit the parent builder while also satisfying an interface, using only immutable fields. This is my class structure:
The Code
public interface FooInterface {
String getFoo();
}
The getFoo logic is very common across all implementations, so I decided to make an Abstract helper to avoid copy-pasting the same code everywhere.
#Data
#SuperBuilder
public abstract class AbstractFoo implements FooInterface {
#Builder.Default
private final String foo = "foo";
}
And the actual Foo implementation:
#Data
#SuperBuilder
public class FooTest extends AbstractFoo {
private final String bar;
}
'Win Condition'
I would like Lombok to
Recognize fields required by the parent class.
Include those fields in the generated Builders of child classes.
In code:
final FooInterface fooTest = FooTest.builder.foo("string").bar("string").build();
assertThat("string").equals(fooTest.getFoo());
assertThat("string").equals(fooTest.getBar());
Attempted Solutions
The problem is, IntelliJ highlights the #Data annotation with this error:
Lombok needs a default constructor in the base class.
If I remove #Data from FooTest I get this error:
There is no default constructor available in base class.
So I removed the #SuperBuilder from AbstractFoo and added a manually-created constructor with all the arguments. The error persists. I've tried other things and annotation combinations, but none have worked.
I also tried -in vain- to set all AbstractFoo fields to protected final, and declare Foo implementations final themselves, which would be consistent with my business rules.
#SuperBuilder isn't supported by current version of IntelliJ IDEA plugin yet.
There's an open issue on project's Github tracker - https://github.com/mplushnikov/lombok-intellij-plugin/issues/513
Although it's targeted for 0.25 release which has been released just a few days ago -
https://github.com/mplushnikov/lombok-intellij-plugin/releases/tag/releasebuild_0.25
Issue still seems to be open and not yet implemented.
I'd suggest to just try version 0.25 and wait for the next release if it won't work.

Inject mocks in mocked abstract class

I have an abstract class which contains logic in concrete methods:
public abstract class AbstractEventHandler implements EventHandler {
private final Dependency dependency;
public AbstractEventHandler(Dependency dependency) {
this.dependency = dependency;
}
#Override
void handleEvent(Event event) {
dependency.doSomeWork();
[...]
doHandleEvent(event);
[...]
}
#Override
void handleOtherEvent(OtherEvent otherEvent) {
dependency.doOtherWork();
[...]
doHandleOtherEvent(event);
[...]
}
protected abstract doHandleEvent(event);
protected abstract doHandleOtherEvent(event);
}
Explored solutions to test my abstract class:
create a dummy implementation of the abstract class (good point for constructors Mocking an abstract class and injecting classes with Mockito annotations?)
test the handleEvent(event) logic in concrete classes but I would have to duplicate the test in every concrete classes (or once, but in which class?)
use PowerMock...
use Mockito to instantiate an implementation of the abstract class and call real methods to test logic in concrete methods
I chose the Mockito solution since it's quick and short (especially if the abstract class contains a lot of abstract methods).
#ExtendWith(MockitoExtension.class)
class AbstractEventHandlerTests {
#Mock
private Dependency dependency;
#InjectMocks
#Mock(answer = Answers.CALLS_REAL_METHODS)
private AbstractEventHandler abstractEventHandler;
Since #InjectMocks is not permitted on a field already annotated with #Mock, how can I inject mocked dependencies in my abstract class?
To reply to comments, I initially wanted to test the behavior of the concrete method in the abstract class because this method has to do some work (using the provided dependency), before calling implementations. To test this behavior, I either had to write tests in each implementation (can have a lot of implementations) or to test it once via the abstract class.
I ended up using the first solution: create an anonymous inner class as an implementation of the abstract class. Thanks.

Y it's not mendatory to implement all interface methods in Child abstract class But Mendatory to implement all Interface methods in Grand Child class?

I made an interface as:
interface Castle
{
public void sad();
public void cool();
}
Then i made a child abstract class of it as:
abstract class Castle2 implements Castle
{
abstract void sad();
}
Here I left the implementation of cool(), and if i complile the above code, it compiled Successfully
But when i added 1 more sub class of Castle2 as:
class Castle3 extends Castle2{
public void sad(){
System.out.println("SAD");
}
public static void main(String...args){
new Castle3().sad();
}
}
And tried to compile the above code then it is not even compiling my code stating the following error
Castle.java:13: error: Castle3 is not abstract and does not override abstract method cool() in Castle
When i run javap tool on class Castle2, then i got the following result
abstract class Castle2 implements Castle {
Castle2();
public void sad();
}
Why Compiler is Forcing me to implement a interface Castle's method in class Castle3 which is not even present in class Castle2?
And Why Compiler is not Forcing me to implement a interface Castle's method in class Castle2?
That is because a concrete class must have implementation because they can be instantiated. Suppose they allow concrete class not to implement all the methods of an interface, there will arise a problem. If in the code we call the unimplemented method, JVM wont be having the address of the unimplemented method.
But abstract classes can not be instantiated. Thats why it is not mandatory to implement methods of an interface by an abstract class.
Because we cannot create object of abstract class. But if abstract class has a subclass, this subclass can be instantiated.
Thats why we need implement all methods in subclass

Calling Abstract classes #Activate method (apache felix)

I have an abstract class that a child class extends. My abstract class has an #Activate method, so does the child class. When OSGi creates my service, it invokes the child class activate method but never the abstract class's activate. Is there any way to force the abstract class's activate to be called by OSGi rather than having the child class manually call the parent activate method?
Here is some code to help elaborate on what I am asking.
#Component(componentAbstract=true, inherit=true)
#Service(value=ISomeInterface)
public abstract class AbstractHello implements ISomeInterface{
#Activate
public void activate(){
System.out.print("Hello ");
}
}
#Component
#Service(Value=ISomeInterface)
public class World extends AbstractHello{
#Activate
public void activate(){
System.out.println("World!");
}
}
The result of the code above would be "World!", rather than "Hello World!".
Initially I thought maybe the child activate method name was clobbering the abstract activate method of the same name. The result is the same even if the abstract class's activate method is given a unique name. Is there any way to have OSGi call the abstract class's activate method for me?
The DS annotation processors only look at the concrete class decorated with #Component. Super classes are not examined. Since the annotation processing is done at build time, super types may come from imported packages which are not chosen until runtime.
Also, the annotation processor generates component description XML from the annotations. So there can only be one activate="methodName" attribute in the XML. If you need the superclass' method called, then you need to call it from the subclass' method.
This has nothing to do with Apache Felix and OSGi, this is caused by poor understanding of Class Inheritance and Method Overriding in Java.
Your World class extends AbstractHello class and overrides its activate() method. If you want the AbstractHello.activate() method to be called then you must call it in
// Annotations excluded for readability.
public class World extends AbstractHello {
public void activate() {
super.activate();
System.out.println("World!");
}
}
OSGi can't help here.
UPDATE
Since the base class is abstract, and you don't have an instance of it, you can't call its method. Neither can OSGi container.

Categories