In our project we haven't Spring, so work like can with that. I found that we have many repetable code.
We have something like that in 5 or more classes
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
#Data
public class QueryParamDto {
#DefaultValue("1");
#QueryParam("sortBy")
private String sortBy;
#DefaultValue("ASC");
#QueryParam("sortDir")
private String sortDir;
...
}
And it would be good to create something like BaseDto class with common fields, but in some classes we have different DefaultValue like
#Data
public class KeywordsDto {
#DefaultValue("5");
#QueryParam("sortBy")
private String sortBy;
...
}
Because of that 'sortBy' can not be coommon fields with common value = 1 from BaseDto.
Maybe there is some variant to oveeride child's fields?
you can just modify it in your child class without the need of overriding, declare it in the father class and give it the value in the child. or check out this answer, maybe could help Overriding member variables in Java ( Variable Hiding)
Related
I have been using a Create Request as shown below and needed to implement an Update Request with some fields non-required.
#Data
public class CreateRequest extends BaseRequest {
#NotEmpty
private String token;
#NotEmpty
private String secret;
}
#Data
public class UpdateRequest extends BaseRequest {
private String token;
private String secret;
}
There are some relations and interfaces that should be used by both request. I think of using Template Design pattern by keeping the shared fields of these requests. Is that suitable for this scenario? Or, what would you suggest?
This may have been what you were getting at in your thoughts on the best approach, but I think your best bet is to have whatever fields/behavior are required for both request types in a shared parent class, and have their individual needs/different fields in a child class.
I am not sure exactly how your optional fields are meant to work conceptually, but if they are optional because of "default" values, then you can have the class with optional fields extend from the one with mandatory fields, and just implement a constructor which calls a super constructor with the default values. For instance if subClass extends parentClass and the constructor of the parent class is two strings, the second of which has a "default" in the child class, something like the following could be done:
public subClass extends parentClass {
subClass(String arg1) {
super(arg1, "default arg2");
}
}
#Data
#Builder
public static class Common {
private String common1;
private String common2;
}
#Getter
public static class Special extends Common {
private String special1;
#Builder
public Special(String common1, String common2, String special1) {
super(common1, common2);
this.special1 = special1;
}
}
The below error occurs :
Error:(149, 9) java: builder() in com.example.home.ExampleDTO.Special cannot override builder() in com.example.home.ExampleDTO.Common
return type com.example.home.ExampleDTO.Special.SpecialBuilder is not compatible with com.example.home.ExampleDTO.Common.CommonBuilder
And when I put (builderMethodName = "b") this parameter in #Builder(Special constructor) then it works fine.
#Builder(builderMethodName = "b")
public Special(String common1, String common2, String special1) {
I have no idea, why the first code gives error.
Please help me out.
Thank you
#Builder creates a static method builder() in both classes; it returns an instance of the respective builder. But the return types of the methods are not compatible, because SpecialBuilder and CommonBuilder are different and unrelated classes: #Builder does not (and can not technically) consider the inheritance relation between the classes. So the compiler complains about two methods with the same name, no arguments, but different return types. This is not possible in Java.
To solve this you have two choices:
Use #SuperBuilder on both classes. #SuperBuilder is designed to work with inheritance.
As you already found out, you can rename the method in one of the classes.
I have a class
public class SomeClass {
#CustomAnnotation1
String stringProperty = "LALALA";
#CustomAnnotation1
int integerProperty;
#CustomAnnotation1
ClassObject classObject;
}
CustomAnnotation1 is a custom annotation defined by me which can be put over any Field. Suppose class ClassObject is something like
public class ClassObject {
#CustomAnnotation1
public String someOtherString;
public String log;
}
What I want to achieve - If my annotation is put on any field which is not a primitive type, I want to access all the fields of that class.
My Approach - Get all the fields annotated with CustomAnnotation1, iterate over all of them and if it is non-primitive, get all the fields inside that class and process.
What I've tried - I am able to get all the elements annotated with my annotation using the below code in my AbstractProcessor class.
Collection<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(CustomAnnotation1.class);
List<VariableElement> variableElements = ElementFilter.fieldsIn(annotatedElements);
Questions -
I've researched a lot about the VariableElement class but unable to find a way to check if the field is primitive or not. Can this be done?
Is there any better approach to achieve this?
VariableElement.asType().getKind().isPrimitive()
I am using Lombok library in my project and I am not able to use a class annotated with #Builder in outer packages.
Is there a way to make the builder public?
MyClass instance = new MyClass.MyClassBuilder().build();
The error is:
'MyClassBuilder()' is not public in
'com.foo.MyClass.MyClassBuilder'. Cannot be accessed
from outside package
#Builder already produces public methods, it's just the constructor that's package-private. The reason is that they intend for you to use the static builder() method, which is public, instead of using the constructor directly:
Foo foo = Foo.builder()
.property("hello, world")
.build();
If you really, really, really want the constructor to be public (there seems to be some suggestion that other reflection-based libraries might require it), then Lombok will never override anything that you've already declared explicitly, so you can declare a skeleton like this with a public constructor and Lombok will fill in the rest, without changing the constructor to package-private or anything.
#Builder
public class Foo
{
private final String property;
public static class FooBuilder
{
public FooBuilder() { }
// Lombok will fill in the fields and methods
}
}
This general strategy of allowing partial implementations to override default behaviour applies to most (maybe all) other Lombok annotations too. If your class is annotated with #ToString but you already declared a toString method, it will leave yours alone.
Just to show you everything that gets generated, I wrote the following class:
#Builder
public class Foo
{
private final String property;
}
I then ran it through delombok to see everything that was generated. As you can see, everything is public:
public class Foo
{
private final String property;
#java.beans.ConstructorProperties({"property"})
Foo(final String property) {
this.property = property;
}
public static FooBuilder builder() {
return new FooBuilder();
}
public static class FooBuilder
{
private String property;
FooBuilder() { }
public FooBuilder property(final String property) {
this.property = property;
return this;
}
public Foo build() {
return new Foo(property);
}
public String toString() {
return "Foo.FooBuilder(property=" + this.property + ")";
}
}
}
The problem is you are using #Builder in the wrong way.
When Builder Pattern is used, you only need to use the static method to invoke it and then build, for example:
MyClass instance = MyClass.builder().build(); .
Please do not new the MyClassBuilder again, it breaks the encapsulation the pattern has since you are directly using the inner MyClassBuilder class. This constructor is been hided from outside, that's why you get the not accessible error. Instead it provides you the static method builder().
I have found this neat workaround:
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
#Builder
public class Customer {
private String id;
private String name;
public static MessageBuilder builder() {return new CustomerBuilder();}
}
The problem with this builder annotation is that, if you delombok you'll see, the generated constructor for the builder has no access indicator (public, private, protected) therefore is only visible within the same package.
This would work if the extended classes were in the same package.
I'm having the same problem and I think that lombok does not support this, for now.
I was able to find the feature request in here https://github.com/rzwitserloot/lombok/issues/1489
My suggestion is to hard implement builder pattern in this class.
as mentioned you can use the builder, now instead of user property builder() will return the instance create so you can treat as normal builder ( no need to use property)
instance = MyClass.MyClassBuilder().property1(value1).property1(value2).build();
Suppose i have a class A:
class A
{
private String value;
private B field;
public C otherField;
}
class C
{
private String otherValue;
}
class B
{
private String name;
}
Now, if i do a A.class.getClasses(), ill get an array with one element (the one which is public, something that makes sense based on what javadoc of Class.java says).
My question is: is there a way to get return a list of public + private fields of a class?
Thanks
The getClasses() method is not the correct way to access the Fields that are part of the A class. You need to use the getDeclaredFields() method to access an array of Field objects representing the fields in the class. From there, you'll need to set the accessibility of the field to true with a call to setAccessible(). There is more information available by looking into the Java Reflection API as well as here
You should notice, that getDeclaredField will only return the fields of the class which are declared in the class. Fields which are inherited from a super class will not be returned. To get all fields of a class you have to iterate over the super classes (using Class.getSuperclass()).