I am learning ByteBuddy and tried with the following code.
I defined an Animal interface with overloaded methods and tried creating a class that overrides those both methods.
That worked fine.
Then I tried to have a field in the generated class and tried to call that field.
I am getting exceptions at that point.
Following is my code:
public interface Animal {
String sound();
String sound(boolean loud);
}
Following is the code snippet from Main::main method.
DynamicType.Unloaded<Animal> dogClassUnloaded = new ByteBuddy()
.subclass(Animal.class)
.name("Dog")
// we can use bytebuddy modifiers
.defineField("colour", String.class, Visibility.PUBLIC, FieldManifestation.FINAL)
.defineConstructor(Visibility.PUBLIC)
// or java modifiers -> Modifier.FINAL + Modifier.PRIVATE like that
.withParameter(String.class, "colour", Modifier.FINAL)
.intercept(
MethodCall.invokeSuper()
.andThen(
// field arguments are denoted by their index
FieldAccessor.ofField("colour").setsArgumentAt(0)
)
)
// There are different ways to match the methods, fields etc.
// https://www.tabnine.com/code/java/classes/net.bytebuddy.matcher.ElementMatchers
// intercepting no argument method
.method(ElementMatchers.named("sound")
.and(ElementMatchers.takesNoArguments())
)
.intercept(FixedValue.value("woof quiet"))
// intercepting overloaded method with arguments
.method(
ElementMatchers.named("sound")
.and(ElementMatchers.takesArgument(0, boolean.class))
)
.intercept(FixedValue.value("woof woof loud"))
.make();
Class<? extends Animal> dogClassLoaded = dogClassUnloaded
.load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Animal dog = dogClassLoaded.getDeclaredConstructor(String.class).newInstance("black");
// Animal dog = dogClassLoaded.getDeclaredConstructor().newInstance();
var x = dog.sound();
var y = dog.sound(true);
System.out.println(x);
System.out.println(y);
// What if Dog.class has other methods / fields not in Animal.class
Field m = dogClassLoaded.getDeclaredField("colour");
String colour = (String) m.get(dog);
System.out.println(colour);
When I tried to execute I get following exception:
Exception in thread "main" java.lang.IllegalStateException: public Dog(java.lang.String) does not accept 0 arguments
at net.bytebuddy.implementation.MethodCall$Appender.toStackManipulation(MethodCall.java:3553)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:3522)
at net.bytebuddy.implementation.bytecode.ByteCodeAppender$Compound.apply(ByteCodeAppender.java:156)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:730)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:715)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:622)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:6043)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2224)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4050)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3734)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3986)
at tutorial.example_1.Main.main(Main.java:55)
It points that the make() method is not compiling properly. That means the class is not building as intended.
I think MethodCall.invokeSuper() is where the issue is since super class is an interface.
What is the solution?
I am using Java-17 and latest version of bytebuddy (1.13)
What you rather want to do is to:
MethodCall.invoke(Object.class.getConstructor()).onSuper()
That invokes the Object constructor on the super object what is the JVM requirement. What you are doing is to look for a method of equal signature to invoke super on which does not exist. It is a synonym for invokeSelf().onSuper().
Related
When learning generics in Kotlin, I read in a book the following :
In general, a class or interface generic type may be prefixed with out if the class has functions that use it as a return type, or if the class has val properties of that type. You can’t, however, use out if the class has function parameters or var properties of that generic type.
I understand what the rule says, but i will be happy to understand (by examples) what may be without this rule (i.e there weren't constraint when using out when declaring a generic Class/Interface), and also why it isn't "dangerous" that the return type can be from type T and still class/Interface can contain out T.
Example where can't understand what is the problem that class property will behave as covariant:
class Pet{....}
class Dog:Pet{...}
class PetSomething <T : Pet>
{
T t;
public fun petDoSomething(T t)
{
.... // what can be the problem here?
}
}
class DogSomething
{
dogDoSomething()
{
d : Dog = Dog()
petDoSomething(d)
//what is the problem here???
}
}
In addition the book display the following code:
abstract class E<out T> (t:T) { val x = t }
and the code is being compiled although generic type is an input of constructor. Doesn't it break the rule?
You quoted: "You can’t, however, use out if the class has function parameters or var properties of that generic type."
A constructor is not a member function or property, so it is not subject to this rule. It is safe to use the type for a parameter at the site of the constructor, because the type is known when you are constructing it.
Consider these classes:
abstract class Pet
class Cat: Pet()
class Dog: Pet()
class PetOwner<out T: Pet>(val pet: T)
When you call the PetOwner constructor and pass in a Cat, the compiler knows you are constructing a PetOwner<out Cat> because it knows the value passed to the constructor satisfies the type of <out Cat>. It doesn't have to upcast Cat to Pet before the object is constructed. Then the constructed object can be safely upcast to PetOwner<Pet> because no T is ever going to be passed to the instance again. There is nothing unsafe that can happen, because no casting is done to the parameter.
Function parameters and var properties would be unsafe for an out type because the object is already constructed and might have been passed to some variable that has already upcast it to something else.
Imagine that the compiler let you define out T for a var property like this:
class PetOwner<out T: Pet>(var pet: T)
Then you could do this:
val catOwner: PetOwner<out Cat> = PetOwner(Cat())
val petOwner: PetOwner<out Pet> = catOwner
petOwner.pet = Dog()
val cat: Cat = catOwner.pet // ClassCastException!
The type safety rules prevent this scenario from being possible. But this isn't possible for a val constructor parameter. There is no way to pass the object to other variables and upcast its type in between passing the parameter to the constructor and having an instance that you can pass around.
The problem is this:
val x = DogSomething()
val y: PetSomething<Pet> = x // would be allowed by out
y.petDoSomething(Cat())
Note that petDoSomething on DogSomething only has to handle Dogs.
and the code is being compiled although generic type is an input of constructor. Doesn't it break the rule?
It doesn't, because the constructor isn't a member in the relevant sense; it couldn't be called on y above.
First lets clearfy what do we get by prefixing a type parameter with out keyword. consider the following class:
class MyList<out T: Number>{
private val list: MutableList<T> = mutableListOf()
operator fun get(index: Int) : T = list[index]
}
out keyword here makes MyList covariant in T, which essentially means you can do the following :
// note that type on left side of = is different than the one on right
val numberList: MyList<Number> = MyList<Int>()
if you remove the out keyword and try to compile again, you will get type mismatch error.
by prefixing the type parameter with out, you are basically declaring the type to be a producer of T's, in the example above MyList is a producer of Numbers.
which means no matter if you instantiate T as Int or Double or some other subtype of Number, you will always be able to get a Number from MyList (Because every subtype of Number is a Number). which also allows you to to do the following:
fun process(list: MyList<Number>) { // do something with every number }
fun main(){
val ints = MyList<Int>()
val doubles = MyList<Double>()
process(ints) // Int is a Number, go ahead and process them as Numbers
process(doubles) // Double is also a Number, no prob here
}
// if you remove out, you can only pass MyList<Number> to process
Now lets answer With out keyword why T should only be in return position? and what can happen without this constraint?, that is if MyList had a function taking T as parameter.
fun add(value: T) { list.add(T) } // MyList has this function
fun main() {
val numbers = getMyList() // numbers can be MyList<Int>, MyList<Double> or something else
numbers.add(someInt) // cant store Int, what if its MyList<Double> ( Int != Double)
numbers.add(someDouble) // cant do this, what if its MyList<Int>
}
// We dont know what type of MyList we going to get
fun getMyList(): MyList<Number>(){
return if(feelingGood) { MyList<Int> () }
else if(feelingOk> { MyList<Double> () }
else { MyList<SomeOtherSubType>() }
}
that is why constraint is required, its basically there to guarantee type safety.
as for abstract class E<out T> (t:T) { val x = t } being compiled, Kotlin In Action has following to say
Note that constructor parameters are in neither the in nor the out
position. Even if a type parameter is declared as out, you can still
use it in a constructor parameter. The variance protects the class
instance from misuse if you’re working with it as an instance of a
more generic type: you just can’t call the potentially dangerous
methods. The constructor isn’t a method that can be called later
(after an instance creation), and therefore it can’t be potentially
dangerous.
Below code throws a java.lang.ClassCastException if I directly use returned object of t.getTheString(implClass.class) in System.out.println() . Although when I assign object to some reference , it works fine, Why ?
interface testInterface{
public String testMethod();
}
class testClass{
public <T extends testInterface> T getTheString(Class< ? extends testInterface> classType){
try {
testInterface obj = classType.newInstance();
return (T)obj;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
class implClass implements testInterface{
#Override
public String testMethod() {
return "Sample String";
}
#Override
public String toString(){
return super.toString() + " Overridden toString()";
}
}
public class genericTest {
public static void main(String args[]){
testClass t = new testClass();
testInterface ti = t.getTheString(implClass.class);
implClass i = t.getTheString(implClass.class);
//System.out.println(i); //<= Works fine
//System.out.println(ti); //<= Works fine
System.out.println(t.getTheString(implClass.class)); // Exception in thread "main" java.lang.ClassCastException: implClass cannot be cast to java.lang.String
}
}
Edit: Many people are getting confused with the question. The use of upper bound is just to create this example . I know this is incorrect .
The doubt is , if you decompile above code , you will see the compiler typecast the t.getTheString(implClass.class) result to String because may be it is unsure of the type probably . My question is wont it be better if it is typecast to Object . In that case it will call toString() method and code will work fine .
Is this an issue with Java Compiler or am i missing any thing here ?
The causes of the problem is already mentioned in other answers - your classType parameter can be any type that implements testInterface, not necessarily T.
Here's a solution.
The problem currently is that classType is not constrained, and without enough context, T cannot be inferred correctly, causing obj to be casted to a wrong type. So if we change the method to this:
public <T extends testInterface> T getTheString(Class<T> classType){
T can be inferred correctly every time from the parameter!
Another solution would be to spoonfeed the compiler with the type you want:
System.out.println((testInterface)t.getTheString(implClass.class));
From your comment to another answer:
If compiler is uncertain about the type why not type cast it to Object
instead of String
There are only two overloads of println that the compiler can choose to call because the others are primitive types. It's either println(Object) or println(String). According to the JLS (section 15.12.2):
This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable There may be more than one such method, in which case the most specific one is chosen.
See this post for more info.
Since String is more specific than Object, the latter is chosen.
Generics are erased at runtime, so what happens is that when JVM needs to choose which variant of System.out.println() to call it has no clue what impl it is and assumes incoming class is String and takes System.out.println(String string) and obviously your implClass instance is not String.
This all comes from you using generics, but not declaring them at class level. Change testClass declaration to declare your generic T
class testClass<T> {
once you do, compiler should start complaining about missing cast when trying to convert testInterface to implClass at
implClass i = t.getTheString(implClass.class);
once you correct it like:
implClass i = (implClass) t.getTheString(implClass.class);
the System.out.println() will start working as expected.
If you are wondering why compiler makes wrong choice of method, think about this:
- Type T is not declared at class level, so when it is erased during compilation, it becomes "anything",
- when compiler is later on trying to find out matching variant of println() method to call, it takes first that matches, since type has become "anything", the first method variant will match
- and in Oracle JDK impl of PrintStream, first impl happens to be the one with String as parameter (check the source code ;)
Works fine for me; renamed and ran as Main.java:
> javac Main.java
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
> java Main
implClass#15dc8287 Overridden toString()
Then:
> javac -Xlint:unchecked Main.java
Main.java:10: warning: [unchecked] unchecked cast
return (T)obj;
^
required: T
found: testInterface
where T is a type-variable:
T extends testInterface declared in method <T>getTheString(Class<? extends testInterface>)
The problem here: in your method getTheString() you cast to T; but there is no guarantee that the incoming class has anything to do with that T! A class that implements that interface isn't necessarily also a T.
And for the exception that you see; as said - not reproducible (some setup problem on your side)?
If you un-comment the two "it works" lines and run genericTest.class through a decompiler (I used http://www.javadecompilers.com/), you'll see:
public static void main(String[] arrstring) {
testClass testClass2 = new testClass();
Object t = testClass2.getTheString(implClass.class);
implClass implClass2 = (implClass)testClass2.getTheString(implClass.class);
System.out.println((Object)implClass2);
System.out.println(t);
System.out.println((String)testClass2.getTheString(implClass.class));
}
The first two calls to println call the method that accepts an Object, which in turn calls toString() and prints the result you expect. In that third println, the compiler expects the result to really be a String, and attempts the cast, which is where you're getting the exception from.
Here is what JavaDoc says:
public final Class <?> getClass()
Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0;
Class<? extends Number> c = n.getClass();
Returns:
The Class object that represents the runtime class of this object.
Now , I understand it is a native method , so it is is implemented in platform-dependent code. But what about the return type of this method.
public final Class<?> getClass()
Also , consider the code:
class Dog
{
#Override
public String toString()
{
return "cat";
}
}
public class Main
{
public static void main(String[] args)
{
Dog d= new Dog();
//Class<Dog> dd = new Dog(); Compile time error
System.out.println(d.getClass());
}
}
Output:
class Dog
So, my query lies in :
Return type of this method
toString method is not called . A similar post on this topic is :
Java. getClass() returns a class, how come I can get a string too?
The commented code which otherwise give compile time error.
The data for each object contains a reference to an object of class java.lang.Class, and this is returned by the method getClass. There is also one java.lang.Class object describing java.lang.Class.
Think of a Class object as the "blueprint" describing a certain class from which objects are being made. It stands to reason that blueprints also need a blueprint of their own (or else how would engineers know how to make blueprints).
These statements try to illustrate this.
Integer integer = 1;
Class<?> clazzInteger = integer.getClass();
System.out.println( "class of integer=" + clazzInteger );
Class<?> clazzClazzInteger = clazzInteger.getClass();
System.out.println( "class of class Integer's class=" + clazzClazzInteger );
String string = "x";
Class<?> clazzString = string.getClass();
System.out.println( "class of string=" + clazzString );
Class<?> clazzClazzString = clazzString.getClass();
System.out.println( "class of class String's class=" + clazzClazzString );
Output:
class of integer=class java.lang.Integer
class of class Integer's class=class java.lang.Class
class of string=class java.lang.String
class of class String's class=class java.lang.Class
A class has a name, just like anything described by a blueprint has a name which is not to be confused with the blueprint itself. If a class object appears in a certain context, its toString() method is called implicitly, and this returns the class' name. If you'd like to print all the nitty-gritty details of a class (akin to printing the blueprint itself) you'd have to write a lot of code - just look at the javadoc for java.lang.Class: there's an awful lot of information to be retrieved (as befits a blueprint).
At this point, we need to differentiate between a type and an instance of the type. Lets explain it with an example.
public class A {
public static void main(String[] args) {
Class<A> typeInformation = A.class; //Type information associated with type `A`
A instanceOfA = new A(); //actual instance of type `A`
}
}
Type
The reference 'typeInformation' in the above code is of the type Class keeping aside the generics for a while. This information will typically be residing in non-heap memory section. Following information is store against each of the type jvm loads :
The fully qualified name of the type
The fully qualified name of the type's direct superclass (unless the type is an interface or class java.lang.Object, neither of which have a superclass)
Whether or not the type is a class or an interface
The type's modifiers ( some subset of` public, abstract, final)
An ordered list of the fully qualified names of any direct superinterfaces
Instance
instaneOfA is a reference to the actual instance of the type A which points to an address in the heap memory.
Return type of getClass() is a generic Class type. Like many other types available in java - String, Integer etc, Class is also a type representing the type information associated.
toString() method is associated and invoked on an instance of the Dog class, not on the Dog type itself.
//Class<Dog> dd = new Dog(); Compile time error
This is due to Type mismatch occuring while assigning the result of expression in the right hand side to the Left Hand Side, which is not of the same type.
Class dd refers to a reference of Class type.
Dog is a different type altogether, and a new Dog() can be assigned to a reference of the type 'Dog'.
This link will help you understand the design aspects of java runtime environment
I have an answer for your Question 3,
This gives compile time error because
Reason 1: For a Class instance, You can only assign class object that represents the Dog class, but you can't assign the Dog class object directly.
For example: Class dd=Dog.class or Class dd=Class.forName("Dog");
is correct syntax.
Reason 2: The class Class is a final class but not a super class for Dog class. You go back to the concept of dynamic method dispatch in java,where you can only assign subclass objects to a superclass variable.
In the following code, it works when passing the method reference variable with the class name, but when passing the reference variable with a user object there is an error.
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void printName() {
System.out.println(name);
}
}
public class Main {
public static void main(String[] args) {
User u1 = new User("AAA");
User u2 = new User("BBB");
User u3 = new User("ZZZ");
List<User> userList = Arrays.asList(u1, u2, u3);
userList.forEach(User::printName); // works
userList.forEach(u1::printName); // compile error
}
}
userList.forEach is expecting a Consumer<? extends User> - in other words, a method which accept a User reference and do something with it.
That could be:
A static method accepting a User parameter, in which case the parameter will be populated with the relevant element in the list on each iteration:
staticMethod(userFromList)
An instance method (of any class) accepting a User parameter, provided with a specific instance to call it on - again, the parameter will be populated with the relevant element:
target.instanceMethod(userFromList)
An instance method on User with no parameters, provided without a specific instance, which case the target of the method call will be the relevant element in the list on each iteration:
userFromList.instanceMethod()
Because you've tried to specify a target and the method doesn't have any parameters, the forEach method has nothing it can do with each element - it can't pass it as an argument because the method doesn't have any parameters, and it can't use it as the method target because you've already specified one.
Your working code shows the third example. Here are two other methods to allow you to demonstrate the first two:
public class UserPrinter {
private final String name;
public UserPrinter(String name) {
this.name;
}
public static void staticPrintUser(User user) {
// Assuming you add a User.getName() method
System.out.println("staticPrintUser: " + user.getName());
}
public void instancePrintUser(User user) {
System.out.println("instancePrintUser (instance " + name + "): "
+ user.getName());
}
}
Then:
userList.forEach(UserPrinter::staticPrintUser); // equivalent to
//userList.forEach(p -> UserPrinter.staticPrintUser(p));
UserPrinter printer = new UserPrinter("my printer");
userList.forEach(printer::instancePrintUser); // equivalent to
//userList.forEach(p -> printer.instancePrintUser(p));
If you really want to call printUser on the same User three times, ignoring the User in the list, you could use:
userList.forEach(ignored -> u1.printName());
Based on http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html we know that method references are similar to following lambdas
method reference ==> lambda
------------------------------------------------------------------------------------
object::method ==> (Foo f, Bar b, Baz z) -> object.method(f,b,z)
SomeClass::staticMethod ==> (Foo f, Bar b, Baz z) -> SomeClass.staticMethod(f,b,z)
SomeClass::instanceMethod ==> (Foo f, Bar b, Baz z) -> f.instanceMethod(b,z)
SomeClass::new ==> (Foo f, Bar b, Baz z) -> new SomeClass(f,b,z)
So your code
userList.forEach(User::printName); // works
can be rewritten as
userList.forEach((User u) -> u.printName()); // works
which is OK because it means that in accept method of Consumer which this lambdas "implements" you will invoke printName() on each User passed to this method.
But in case of
userList.forEach(u1::printName); // compile error
this code represents following lambda
userList.forEach((User u) -> u1.printName(u)); // compile error
// ^^^ // method doesn't accept User argument
so you are trying to invoke printName from instance held by u1 reference and pass each User from list as this method argument, but as you see
public void printName()
can't accept instance of User as its argument, which is why you are seeing compile time error.
This
u1::printName
is a method reference to be invoked on the object referenced by ui. The compiler doesn't know how to interpret the argument it should pass to the Consumer lambda. Its best guess is that it should be passed as
u1.printName(arg);
but such a method doesn't exist.
The method reference
u1::printName
is essentially equivalent to this lambda:
() -> u1.printName()
This is because printName doesn't have any arguments. If you had a printNameWithWidth(int width) method, then u1::printNameWithWidth would be equivalent to
(int width) -> u1.printNameWithWidth(width)
But the point is that in neither case is a User one of the arguments, since you've already told it which User to use (i.e. u1). forEach doesn't like that. It needs a lambda (or the equivalent) with a User (or whatever other element type) as an argument.
This:
User::printName
is equivalent to
(User x) -> x.printName()
which is why it works.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
public class Testing {
public static void main(String[] args) {
List<B> list = new ArrayList<B>();
B b1=new B(); b1.setName("Durgesh");
B b2=new B(); b2.setName("Val");
list.add(b1);list.add(b2);
MyInterface<B> my1 = B :: printName;
my1.dummyDisplay(b1,b2);
MyInterface<B> my2 = (a,b) -> a.printName(b);
my2.dummyDisplay(b1,b2);
// MyInterface<B> my3 = b1::printName; //compilation error
}
}
class B{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName(B b) {
System.out.println(this.name + b.name);
}
}
#FunctionalInterface
interface MyInterface <T> {
public void dummyDisplay(T s, T t);
}
Below lines of code works fine even though printName method of class B accepts only 1 parameter while method dummyDisplay accepts 2 parameters. It is because when we call dummyDisplay ( of functional interface ) method with 2 arguments, compiler uses one argument to invoke printName method and another argument is passed as an argument to printName method. That means (arg1).printName(arg2). Notice the usage of "this" keyword in the method printName.So always remember that in such kinds of method references, number of paramters of the method to be called(printName) should always be 1 less than the number of parameters used in method(dummyDisplay) of functional interface. Such kind of method reference is very frequently used when dealing with POJO classes where we use getters(no arg) of the classes given the functional interface(say Function/Consumer - having methods with 1 parameter).
MyInterface<B> my1 = B :: printName;
my1.dummyDisplay(b1,b2);
I hope you understand this concept.
Now coming to below lines of code. This code is just a replacement of method reference which we discussed just now. Here, since method declared in functional interface has 2 parameters, so we have to and had to use 2 arguments in lambda expression (in this case its a and b). Then a.printName(b) will be written as the definition of interface method (dummyDisplay). Its straight forward. Lambda expression can be used any where provided Functional interfaces(off course).
MyInterface<B> my2 = (a,b) -> a.printName(b);
my2.dummyDisplay(b1,b2);
Now coming to last piece of code. We get compilation error because compiler expects exactly the same number of parameters in printName method of class B which are there in the method of functional interface. Normally this kind of method reference is used just to call any random method of any class which accepts some parameters and does some processing on the accepted data. eg. say add/multiply/divide methods present in the class Calculate or the compare method of Comparator functional interface. In all these cases, method definition does not use "this" keyword. It simply accepts some parameters and performs some task on them. I hope you guys got something out of it.
MyInterface<B> my3 = b1::printName; //compilation error
Having said this, Now lets come to your question,
userList.forEach(User::printName);
works fine because, forEach method internally calls a method accept(arg1) of Consumer interface and your user-defined method printName has no args. So as per my above explanation, it is correct and the compiler does not complaint.
and
userList.forEach(u1::printName);
gives compilation error, because you are using object u1 while referencing instance method printName . So compiler expects same number of parameters of printName method as that there are in accept method of Consumer interface. So it will try to find printName(User param1) from your class User. And since it is not found, compiler complaints about the same.
I hope this helps you guys. Also let me know if I have missed anything or if I have said some thing wrong.
I think it's the key point of usage of method references in Java. I had really difficulty in learning it. #Pshemo's answer is really nice source for this case. Besides, the following image excerpted from Java 8 in Action is helpful to remember.
suppose i have classA and classB(generic), reference http://www.exampledepot.com/egs/java.lang.reflect/Constructors.html
I am passing a customobject from classA to classB, now i am wanting constructor of customobject in classB and call it
classA
customclass objCustomclass;
classB mClassB;
mClassB.getConstructorAndCall(objCustomclass);
classB
public void getConstructorAndCall(Object objCustomclass);
try {
Object filledObject = objCustomclass.getClass().newInstance();
// here i need to call filledObject's contructor
} catch (Exception e) { }
I am able to create object as the instance of custom object but what about constructor.
note: getConstructorAndCall() is a commom method and in that object is of unknown type that means any class can pass its object.
Thanks.
well if the constructor is empty, then I think that what you have should run the constructor. Anything more complicated, like, passing parameters to the function can be done through:
Constructor[] constructors = objCustomClass.getClass().getConstructors()
for (int i = 0; i < constructors.length; i++) {
Constructor c = constructors[i];
Class[] paramTypes = c.getParameterTypes();
Object[] params;
// do fancy stuff here - it helps if you know what the constructors take beforehand
Object filledObject = c.newInstance(params);
}
Constructor with parameters
From the JavaDoc for java.lang.Class ( http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#newInstance() ):
Creates a new instance of the class represented by this Class object.
The class is instantiated as if by a new expression with an empty
argument list. The class is initialized if it has not already been
initialized.
I. E. the newInstance() method always uses the default constructor.
Most times I see people using new instance and requiring a particular constructor or signature, it happens to be due to a lack on the solution design. You might want to double check if a pattern applies to the solution you need.