Lombok extension methods: Prevalence/priority? - java

First off: I absolutely LOVE Project Lombok. Awesome tool! There's so many excellent aspects to this 'compile time' library.
Loving the #ExtensionMethods, I have already hit this 'feature' a few times, so now it's time for me to ask this question:
Suppose I have the following classes:
#UtilityClass
public class AObject {
static public String message(final Object pObject) {
return "AObject = " + (pObject != null);
}
}
#UtilityClass
public class AString {
static public String message(final String pObject) {
return "AString = " + (pObject != null);
}
}
#ExtensionMethod({ AObject.class, AString.class })
public class Run_Object_String {
public static void main(final String[] args) {
System.out.println("\nRun_Object_String.main()");
final String s = "Bier!";
final Object o = new Object();
System.out.println("Testing s: " + s.message());
System.out.println("Testing o: " + o.message());
System.out.println("Testing s: " + s.message());
}
}
#ExtensionMethod({ AString.class, AObject.class })
public class Run_String_Object {
public static void main(final String[] args) {
System.out.println("\nRun_String_Object.main()");
final String s = "Bier!";
final Object o = new Object();
System.out.println("Testing s: " + s.message());
System.out.println("Testing o: " + o.message());
System.out.println("Testing s: " + s.message());
}
}
public class ClassPrevalenceTest {
public static void main(final String[] args) {
Run_Object_String.main(args);
Run_String_Object.main(args);
}
}
With the output:
Run_Object_String.main()
Testing s: AObject = true
Testing o: AObject = true
Testing s: AObject = true
Run_String_Object.main()
Testing s: AString = true
Testing o: AObject = true
Testing s: AString = true
Why is this?
Why is the message(String) not called in the first example, even though it has a better method signature fit than message(Object)?
Why is #ExtensionMethod dependent on sequence of the arguments?
Here's what I blindly assume:
when parsing for ExtensionMethods, Lombok will process annotation values from left to right
For Run_Object_String that means: first AObject, then AString
For Run_String_Object that means: first AString, then AObject
Object-String: When patching AObject into class Run_Object_String, the message(Object) method will be added. And when patching in AString with the message(String) method, it will not be added.
Presumably because the message(Object) also matches a call to message(String), so message(String) will not be added.
String-Object: When patching AString into class Run_String_Object, the message(String) method will be added.
When patching in AObject class with message(Object), the old and present message(String) method will NOT accept the call message(Object), thus the method message(Object) will be added.
So, apart from taking great care of what order I add the #UtilityClass references, are there any other solutions to this?
Can the Lombok preprocessor be extended and made more sensible when adding in extension methods?
Do you guys have any suggestions regarding this, or an explanation of what is really happening (as opposed to my assumptions)

This is a fascinating use of Lombok I wasn't aware of. The best place I think you could delve to find your answers is the source itself since the docs on this experimental work seems pretty light, understandably.
Take a look on git here: HandleExtensionMethod.
I am guessing based on the logic that the area that's effectively "fitting" the right method from the annotation is as below..
Instead of trying for a "best" fit, it seems to be aiming for a "first" fit.
That is, it appears to iterate over List<Extension> extensions. Since it's a Java list, we assume ordering is preserved in the order the extensions were specified in the original annotation.
It appears to simply work in order of the list and return as soon as something matches the right method and type shape.
Types types = Types.instance(annotationNode.getContext());
for (Extension extension : extensions) {
TypeSymbol extensionProvider = extension.extensionProvider;
if (surroundingTypeSymbol == extensionProvider) continue;
for (MethodSymbol extensionMethod : extension.extensionMethods) {
if (!methodName.equals(extensionMethod.name.toString())) continue;
Type extensionMethodType = extensionMethod.type;
if (!MethodType.class.isInstance(extensionMethodType) && !ForAll.class.isInstance(extensionMethodType)) continue;
Type firstArgType = types.erasure(extensionMethodType.asMethodType().argtypes.get(0));
if (!types.isAssignable(receiverType, firstArgType)) continue;
methodCall.args = methodCall.args.prepend(receiver);
methodCall.meth = chainDotsString(annotationNode, extensionProvider.toString() + "." + methodName);
recursiveSetGeneratedBy(methodCall.meth, methodCallNode);
return;
}
}
You can look at the rest of the code for other insight as there doesn't seem to be too much there (i.e. number of lines) to look at, though admittedly it's an impressive enough a feat to do in that space.

Related

How do I get around the limitation of having to place super constructor in the first line?

public NoWheelsException(Car[] carArray){
String holder = "";
for (int i=0; i<carArray.length; i++) {
if (i == carArray.length - 1) {
holder = holder + carArray[i].name;
}else{
holder = holder + carArray[i].name + ", ";
}
}
String message = holder + " has/have no wheels.";
super(message);
}
Written above is the ideal scenario that I'd have for my code, with the super constructor at the end. Although, since super has to be the first statement, I cannot figure out how to develop the string out of the array inline. I can't straight up use .toString() as there's certain criteria into what the string should look like. I've managed to figure out everything regarding Exceptions except this itty bitty detail. Any help would be greatly appreciated!
Update
I got suggested to try Strin.join in order to link them together although unfortunately the object reference names differ from the name variable in the array objects...
One way is to create a private static method, since static methods exist irrespective of constructors and instantiation:
public NoWheelsException(Car[] carArray){
super(buildMessageFrom(carArray));
}
private static String buildMessageFrom(Car[] cars) {
StringBuilder message = new StringBuilder();
String separator = "";
for (Car car : cars) {
message.append(separator);
message.append(car.name);
separator = ", ";
}
return message.toString();
}
(When building a String in a loop, StringBuilder is much more efficient than string concatenation. Each iteration of ‘holder = holder + …’ would create a new String object that eventually needs to be garbage collected.)
If you’re comfortable with Streams, you can do it all on one line:
public NoWheelsException(Car[] carArray){
super(Arrays.stream(carArray).map(c -> c.name).collect(Collectors.joining(", ")));
}

passing static method as parameter in Java

Hello I'm testing the class that has some validating methods and I've been wondering if there is a way to reduce the duplicated code.
#Test
void testCorrectEmailValidator() {
List<String> correctEmails = Arrays.asList("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
for (String email : correctEmails) {
boolean isValid = UserCredentialsValidator.emailValidator(email);
System.out.println("Email is valid: " + email + ": " + isValid);
assertTrue(isValid);
}
}
#Test
void testCorrectUsernameValidator() {
List<String> correctUsernames = Arrays.asList("username", "123username", "username3", "user2name",
"USERNAME", "USERNAME123", "123USERNAME123", "2uSERname33");
for(String username : correctUsernames) {
boolean isValid = UserCredentialsValidator.usernameValidation(username, userList);
System.out.println("Username is valid: " + username + " : " + isValid);
assertTrue(isValid);
}
}
I also have validators for other fields such as username etc. I was thinking about implementing a helper method that would accept: tested credential as String, List but I've got a problem with last parameter - a validating method, not sure how to pass that.
The code i would like to replace with some method is the for loop.
I am afraid your tests are of low quality.
The problems that should be fixed immediately include
UserCredentialsValidator.usernameValidation(username, userList); The method shouldn't take the second argument. The place from where that list is retrieved should be concealed from the API consumer.
List<String> correctEmails = Arrays.asList(...) and List<String> correctUsernames = Arrays.asList(...) should be removed. You'd better make the tests parameterised with #ParameterizedTest and #ValueSource.
I'd rather remove the System.out.println statements. They make little sense in tests.
#ParameterizedTest
#ValueSource(strings = {"test#test.com", "test123#test123.com"})
void testUserEmailValidationWithValidUserEmailShouldPass(String validUserEmail) {
boolean isValid = UserCredentialsValidator.emailValidator(validUserEmail);
assertTrue(isValid);
}
#ParameterizedTest
#ValueSource(strings = {"username", "123username"})
void testUserNameValidationWithValidUserNameShouldPass(String validUserName) {
boolean isValid = UserCredentialsValidator.usernameValidation(validUserName);
assertTrue(isValid);
}
Now there is nothing to reduce.
As I already stated in my comment to your question, I'm not sure rearranging your code would help much. However, as a comparision here's a Java8+ version which uses a common method:
#Test
void testCorrectEmailValidator() {
List<String> correctEmails = Arrays.asList("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
testValidator( "Email", correctEmails , email -> UserCredentialsValidator.emailValidator(email) );
}
#Test
void testCorrectUsernameValidator() {
List<String> correctUsernames = Arrays.asList("username", "123username", "username3", "user2name",
"USERNAME", "USERNAME123", "123USERNAME123", "2uSERname33");
//I don't know where userList does come from but it would need to be final to be used here
testValidator( "Username", correctUsernames, username -> UserCredentialsValidator.usernameValidation(username, userList) );
}
void testValidator( String name, List<String> data, Predicate<String> validator) {
for( String element : data ) {
boolean isValid = validator.test( element );
System.out.println( name + " is valid: " + element + " : " + isValid);
assertTrue(isValid);
}
}
In that particular case both approaches would be 23 lines long while the second one might be easier to reuse but harder to understand and less flexible (e.g. if you'd need to pass additional parameters etc.)
Use parameterized tests:
static Stream<String> emailsSource() {
return Stream.of("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
}
#Test
#MethodSource("emailsSource")
void testCorrectEmailValidator(String email) {
boolean isValid = UserCredentialsValidator.emailValidator(email);
assertTrue(isValid);
}
Repeat for usernameSource etc. IMHO, this is sufficient to eliminate duplicities.
However if you want to go further and generalize it, use method references. I wouldn't recommend it though.
static Stream<Pair<String,Predicate<String>>> allSources() {
return Stream.of(
Pair.of("test#test.com", UserCredentialsValidator::emailValidator),
Pair.of("username", UserCredentialsValidator::usernameValidationOneArg), // like usernameValidation but with argument userList fixed
...
);
}
#Test
#MethodSource("allSources")
void testAll(Pair<String,Predicate<String>> p) {
String s = p.getLeft();
Predicate<String> test = p.getRight();
boolean isValid = test.apply(email);
assertTrue(isValid);
}
Fact that you're struggling to test is indicating a design smell.
Its good time for you to explore strategy design pattern here.
Basically you main code would look something like
interface IValidator {
boolean isValid(List<String> yourDataToBeValidated);
}
Now create multiple validator classes for different fields like email, username etc.
class EmailValidator implements IValidator {
boolean isValid(List<String> yourDataToBeValidated){
//Email specific validations goes here
}
}
You can create more validators as you need on the go.
Now in your unit tests create new EmailValidator() or new UsernameValidator() and pass your emailIds or usernames to be isValid() method, something like below :
boolean isValid = new EmailValidator().isValid(Arrays.asList("test#test.com", "test123#test123.com");
assertTrue(isValid);

I am making a safe, compile-time String.format(...) equivalent. An issue still persist

Most people understand the innate benefits that enum brings into a program verses the use of int or String. See here and here if you don't know. Anyway, I came across a problem that I wanted to solve that kind of is on the same playing field as using int or String to represent a constant instead of using an enum. This deals specifically with String.format(...).
With String.format, there seems to be a large opening for programmatic error that isn't found at compile-time. This can make fixing errors more complex and / or take longer.
This was the issue for me that I set out to fix (or hack a solution). I came close, but I am not close enough. For this problem, this is more certainly over-engineered. I understand that, but I just want to find a good compile-time solution to this, that provides the least amount of boiler-plate code.
I was writing some non-production code just to write code with the following rules.
Abstraction was key.
Readability was very important
Yet the simplest way to the above was preferred.
I am running on...
Java 7 / JDK 1.7
Android Studio 0.8.2
These are unsatisfactory
Is there a typesafe alternative to String.format(...)
How to get string.format to complain at compile time
My Solution
My solution uses the same idea that enums do. You should use enum types any time you need to represent a fixed set of constants...data sets where you know all possible values at compile time(docs.oracle.com). The first argument in String.format seems to fit that bill. You know the whole string beforehand, and you can split it up into several parts (or just one), so it can be represented as a fixed set of "constants".
By the way, my project is a simple calculator that you probably seen online already - 2 input numbers, 1 result, and 4 buttons (+, -, ×, and ÷). I also have a second duplicate calculator that has only 1 input number, but everything else is the same
Enum - Expression.java & DogeExpression.java
public enum Expression implements IExpression {
Number1 ("%s"),
Operator (" %s "),
Number2 ("%s"),
Result (" = %s");
protected String defaultFormat;
protected String updatedString = "";
private Expression(String format) { this.defaultFormat = format; }
// I think implementing this in ever enum is a necessary evil. Could use a switch statement instead. But it would be nice to have a default update method that you could overload if needed. Just wish the variables could be hidden.
public <T> boolean update(T value) {
String replaceValue
= this.equals(Expression.Operator)
? value.toString()
: Number.parse(value.toString()).toString();
this.updatedString = this.defaultFormat.replace("%s", replaceValue);
return true;
}
}
...and...
public enum DogeExpression implements IExpression {
Total ("Wow. Such Calculation. %s");
// Same general code as public enum Expression
}
Current Issue
IExpression.java - This is a HUGE issue. Without this fixed, my solution cannot work!!
public interface IExpression {
public <T> boolean update(T Value);
class Update { // I cannot have static methods in interfaces in Java 7. Workaround
public static String print() {
String replacedString = "";
// for (Expression expression : Expression.values()) { // ISSUE!! Switch to this for Expression
for (DogeExpression expression : DogeExpression.values()) {
replacedString += expression.updatedString;
}
return replacedString;
}
}
}
So Why Is This An Issues
With IExpression.java, this had to hacked to work with Java 7. I feel that Java 8 would have played a lot nicer with me. However, the issue I am having is paramount to getting my current implementation working The issue is that IExpression does not know which enum to iterate through. So I have to comment / uncomment code to get it to work now.
How can I fix the above issue??
How about something like this:
public enum Operator {
addition("+"),
subtraction("-"),
multiplication("x"),
division("÷");
private final String expressed;
private Operator(String expressed) { this.expressed = expressed; }
public String expressedAs() { return this.expressed; }
}
public class ExpressionBuilder {
private Number n1;
private Number n2;
private Operator o1;
private Number r;
public void setN1(Number n1) { this.n1 = n1; }
public void setN2(Number n2) { this.n2 = n2; }
public void setO1(Operator o1) { this.o1 = o1; }
public void setR(Number r) { this.r = r; }
public String build() {
final StringBuilder sb = new StringBuilder();
sb.append(format(n1));
sb.append(o1.expressedAs());
sb.append(format(n2));
sb.append(" = ");
sb.append(format(r));
return sb.toString();
}
private String format(Number n) {
return n.toString(); // Could use java.text.NumberFormat
}
}

Java reflection nested methods not modifying underlying object

I am taking in an array of methods and I want to chain them together to modify an object that I am working in.
For example I start with
"getStuff().get(1).get(3).setMoreStuff().put(stuff,6)"
I split it into an array called methods, and clean up the parameters inside each method and I try to modify this.
Object res = this;
String[] methods = targetString.split("\\.(?=\\D)");
for (String m : methods){
List<Object> params = new ArrayList<Object>();
List<Object> params = new ArrayList<Object>();
for (String p : m.split("\\(|,|\\)")) {
try {
if (p.indexOf(".") != -1){
double tempD = Double.parseDouble(p);
params.add(tempD);
} else {
int tempP = Integer.parseInt(p);
params.add(tempP);
}
} catch (Exception ex) { //not a number
params.add(p);
}
}
switch (params.size()) {
case 1:
res = res.getClass().getMethod(
params.get(0)
).invoke(res);
break;
case 2:
res = res.getClass().getMethod(
params.get(0),
params.get(1).getClass()
).invoke(res, params.get(1));
break;
case 3:
res = res.getClass().getMethod(
params.get(0),
params.get(1).getClass(),
params.get(2).getClass()
).invoke(res, params.get(1), params.get(2));
break;
}
in the end I notice that res has been modified the way that I expect. All the getters and setters are called correctly. But of course the underlying object "this" refers to has not been changed!
I guess I'm just calling the getters and setters of the copy I made in the first line!
now I can't just use
this.getClass().getMethod(...).invoke(...)
because I need to call the same getMethod on the object returned by this call.
To clarify:
Object res = this;
creates a "pointer" to this. So that when I call
res.getStuff().setStuff(foo)
this will also be modified.
but it seem that when I call
res = res.getStuff();
res = res.setStuff();
like I do in my loop,
this does not modify the underlying object this refers to?
Edit: Included more code as per request.
Edit2: added anther example, to clarify my problem.
Edit3: tried to add more code, its a bit hard to add a working program without including every class
Your general approach should be fine (although your approach to parameter conversion is somewhat ugly) - it's the specifics that are presumably causing you problems. Here's a short but complete program demonstrating calling methods and then seeing the difference afterwards:
import java.lang.reflect.*;
class Person {
private String name = "default";
public String getName() {
return name;
}
// Obviously this would normally take a parameter
public void setName() {
name = "name has been set";
}
}
class Test {
private Person person = new Person();
public Person getPerson() {
return person;
}
// Note that we're only declaring throws Exception for convenience
// here - diagnostic code only, *not* production code!
public void callMethods(String... methodNames) throws Exception {
Object res = this;
for (String methodName : methodNames) {
Method method = res.getClass().getMethod(methodName);
res = method.invoke(res);
}
}
public static void main(String[] args) throws Exception {
Test test = new Test();
test.callMethods("getPerson", "setName");
System.out.println(test.getPerson().getName());
}
}
The output is "name has been set" just as I'd expect. So see if you can simplify your code bit by bit, removing extra dependencies etc until you've got something similarly short but complete, but which doesn't work. I suspect you'll actually find the problem as you go.
Object does not change reference, its VALUE changes. So if you will call this.get("some key"), you will get value that the same value that you put using reflection.
Right?

Invoking a same method just once in java

I have four different classes classA, classB, classC and classD. All the four classes have the same static method search() which takes two string parameters. If i want to invoke static method search in four different classes from main class at once. How can I do that. For now my code is as follows for main class. I need to execute the same thing for other 3 classes also. How can i do that and display the results of other 3 in the same way as for classA. The way search is done in 4 classes r different but they should give the same result.
Main() {
Object[] zy;
for (String pattern : Read.arrayList) {
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern + " match is found at index : "+ results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
I strongly recommend you change this to non-static methods. Look how easy and nice is when you will seperate an interface:
public interface Common {
List<Integer> findAll(String pattern, String dataToSearch);
}
public class A implements Common ...
public class B implements Common ...
public class C implements Common ...
public class D implements Common ...
// in main:
List<Common> allYourClasses = new ArrayList<Common>();
allYourClasses.add(new A());
allYourClasses.add(new B());
allYourClasses.add(new C());
allYourClasses.add(new D());
List<Integer> result = new ArrayList<Integer>();
for (Common c : allYourClasses) {
result.addAll(c.findAll(pattern, dataToSearch));
}
1 - You should NOT do this. Avoid static methods. One of the reason being they can not be called without the exact class. A group of classes that implement a simple interfaces will work faster, safer and better in every way
2 - You can (but you shouldn't) do something like this:
for (Class<?> clazz : new Class[] { ClassA.class, ClassB.class,
ClassC.class }) {
Object[] zy = null;
String dataToSearch = "";
String[] arrayList = { "a" };
for (String pattern : arrayList) {
List<Integer> results = findAllForClass(clazz, pattern,
dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern
+ " match is found at index : " + results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
#SuppressWarnings("unchecked")
public static List<Integer> findAllForClass(Class<?> clazz, String pattern,
String dataToSearch) {
List<Integer> list = null;
try {
list = (List<Integer>) clazz.getDeclaredMethod("findAll", String.class,
String.class).invoke(null, pattern, dataToSearch);
} catch (Exception e) {
list = Collections.emptyList();
}
return list;
}
You see the #supresswarning and the try/catch? well, this is a hint: is telling you you this code is at least suspicious. It is in fact unsafe, non well performant, and is a stupid workaround.
(But we all did something like that once in our lives)
I can't really figure out why would anyone do that.
That said, you could have a method taking a Class as a parameter and calling the method explicitly by name (getMethod.../invoke()).
That puts you back in non static world and you can iterate over the classes you want to invoke. (But again, why use statics in the first place?)
Pseudo untested code:
public void invokeStatic(Class clazz, String method, Class<?> paramsTypes[], Object[] params) {
Method method = clazz.getMethod(method, paramsType);
method.invoke(params);
}
If you want to group all of the results together, just keep adding results to your list:
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
results.addAll(ClassB.findAll(pattern, dataToSearch));
// etc.

Categories