I have the following code:
public class BiPredicateTest {
public static void main(String[] args) {
BiPredicate<List<Integer>, Integer> listContains = List::contains;
List aList = Arrays.asList(10, 20, 30);
System.out.println(listContains.test(aList, 20)); // prints true magically?
}
}
In the statement listContains.test(aList, 20), how is it that the method "contains" is getting called on the first argument and the second argument is passed in as a parameter? Something equivalent to:
System.out.println(aList.contains(20));
In other words, how does the statement listContains.test(aList, 20) get translated to aList.contains(20)?
Is this how java 8 BiPredicate work? Could someone explain how the magic is happening (with some references)?
This is not a duplicate post. This differs from "What does “an Arbitrary Object of a Particular Type” mean in java 8?" in that its not explicitly passing method reference around. It is very clear how method reference is being passed around in the post you reference. The array instance on which the method is being called is passed as an argument to Arrays.sort(). In my case, how the method "contains" is being called on aList is not apparent. I am looking for a reference or explanation as to how its working.
It seems some individuals prefer to down vote instead of provide reference or explanation. They give the impression that they have knowledge but refuse to share it.
BiPredicate is an interface which has only one method, test.
public interface BiPredicate<A,B> {
boolean test(A a, B b);
}
Interfaces which have only one method are called functional interfaces. Previous to Java 8, you would often times have to implement these interfaces using an anonymous class, just to create a wrapper for a certain method call with the same signature. Like this:
BiPredicate<List<Integer>,Integer> listContains = new BiPredicate<>() {
#Override
public boolean test(List<Integer> list, Integer num) {
return list.contains(num);
}
};
In Java 8, method references were added, which allowed for a much shorter syntax and more efficient bytecode for this pattern. In a method reference, you can specify a method or constructor which has the same signature as the type arguments for the interface. When you make a method reference using a class type, it assigns the class type as the first generic argument of the functional interface being used. This means whatever parameter which uses that generic type will need to be an instance of the class.
Even if the instance method normally doesn't take any parameters, a method reference can still be used which takes an instance as the parameter. For example:
Predicate<String> pred = String::isEmpty;
pred.test(""); // true
For more information, see the Java Tutorial for Method References.
Related
According to Oracle Documentation, the String::compareToIgnoreCase is also a valid method reference, my question is that compareToIgnoreCase is not a static method, in other words, compareToIgnoreCase must be attached to a specific String instance. So how does JDK know which instance of String I refer when I use String::compareToIgnoreCase ?
Consider the following example using toUpperCase which is also an instance method.
It works in this case because the Stream item that is being handled is of the same type as the class of the method being invoked. So the item actually invokes the method directly.
So for
Stream.of("abcde").map(String::toUpperCase).forEach(System.out::println);
the String::toUpperCase call will be the same as "abcde".toUpperCase()
If you did something like this:
Stream.of("abcde").map(OtherClass::toUpperCase).forEach(System.out::println);
"abcde" is not a type of OtherClass so the OtherClass would need to look like the following for the stream to work.
class OtherClass {
public static String toUpperCase(String s) {
return s.toUpperCase();
}
}
String::compareToIgnoreCase is not used such as str1.compareToIgnoreCase(str2) would be.
It actually is used as a comparator.
E.g. you could compare it to
Arrays.sort(someIntegerArray, Collections.reverseOrder())
but in this case it would be
Arrays.sort(someStringArray, String::compareToIgnoreCase)
It is like there is an additional parameter, the actual instance, involved.
Example for String::compareToIgnoreCase:
ToIntBiFunction<String, String> func = String::compareToIgnoreCase;
int result = func.applyAsInt("ab", "cd"); // some negative number expected
We get a ToIntBiFunction - a two parameter function returning int - since the result is an int, the first parameter correspond to this of compareToIgnoreCase and the second function parameter is the parameter passed to compareToIgnoreCase.
maybe a bit easier:
ToIntFunction<String> f = String::length; // function accepting String, returning int
int length = f.applyAsInt("abc"); // 3
length does not accept any parameter, but the first argument of the function is used as the instance length is called on
The examples above are very abstract, just to show the types involved. The functions are mostly used directly in some method call, no need to use an intermediate variable
This question already has answers here:
Java implicit "this" parameter in method?
(2 answers)
Closed 4 years ago.
I'm trying to understand how method references work in java.
At first sight it is pretty straightforward. But not when it comes to such things:
There is a method in Foo class:
public class Foo {
public Foo merge(Foo another) {
//some logic
}
}
And in another class Bar there is a method like this:
public class Bar {
public void function(BiFunction<Foo, Foo, Foo> biFunction) {
//some logic
}
}
And a method reference is used:
new Bar().function(Foo::merge);
It complies and works, but I don't understand how does it match this:
Foo merge(Foo another)
to BiFunction method:
R apply(T t, U u);
???
There is an implicit this argument on instance methods. This is defined §3.7 of the JVM specification:
The invocation is set up by first pushing a reference to the current instance, this, on to the operand stack. The method invocation's arguments, int values 12 and 13, are then pushed. When the frame for the addTwo method is created, the arguments passed to the method become the initial values of the new frame's local variables. That is, the reference for this and the two arguments, pushed onto the operand stack by the invoker, will become the initial values of local variables 0, 1, and 2 of the invoked method.
To understand why method invocation is done this way, we need to understand how the JVM stores code in memory. The code and the data of an object are separated. In fact, all methods of one class (static and non-static) are stored in the same place, the method area (§2.5.4 of JVM spec). This allows to store each method only once instead of re-storing them for each instance of a class over and over again. When a method like
someObject.doSomethingWith(someOtherObject);
is called, it gets actually compiled to something that looks more like
doSomething(someObject, someOtherObject);
Most Java-programmers would agree that someObject.doSomethingWith(someOtherObject) has a "lower cognitive complexity": we do something with someObject that involves someOtherObject. The center of this action is someObject, where someOtherObject is just a means to an end.
With doSomethingWith(someObject, someOtherObject), you do not transport this semantics of someObject being the center of the action.
So in essence, we write the first version, but the computer prefers the second version.
As was pointed out by #FedericoPeraltaSchaffner, you can even write the implicit this parameter explicitly since Java 8. The exact definition is given in JLS, §8.4.1:
The receiver parameter is an optional syntactic device for an instance method or an inner class's constructor. For an instance method, the receiver parameter represents the object for which the method is invoked. For an inner class's constructor, the receiver parameter represents the immediately enclosing instance of the newly constructed object. Either way, the receiver parameter exists solely to allow the type of the represented object to be denoted in source code, so that the type may be annotated. The receiver parameter is not a formal parameter; more precisely, it is not a declaration of any kind of variable (§4.12.3), it is never bound to any value passed as an argument in a method invocation expression or qualified class instance creation expression, and it has no effect whatsoever at run time.
The receiver parameter must be of the type of the class and must be named this.
This means that
public String doSomethingWith(SomeOtherClass other) { ... }
and
public String doSomethingWith(SomeClass this, SomeOtherClass other) { ... }
will have the same semantic meaning, but the latter allows for e.g. annotations.
I find it easier to understand with different types :
public class A {
public void test(){
function(A::merge);
}
public void function(BiFunction<A, B, C> f){
}
public C merge(B i){
return null;
}
class B{}
class C{}
}
We can know see that using a method reference Test::merge instead of a reference on an instance will implicitly use this as the first value.
15.13.3. Run-Time Evaluation of Method References
If the form is ReferenceType :: [TypeArguments] Identifier
[...]
If the compile-time declaration is an instance method, then the target reference is the first formal parameter of the invocation method. Otherwise, there is no target reference.
And we can find some example using this behavior on the following subject:
The JLS - 15.13.1. Compile-Time Declaration of a Method Reference mention:
A method reference expression of the form ReferenceType::[TypeArguments] Identifier can be interpreted in different ways.
- If Identifier refers to an instance method, then the implicit lambda expression has an extra parameter [...]
- if Identifier refers to a static method. It is possible for ReferenceType to have both kinds of applicable methods, so the search algorithm described above identifies them separately, since there are different parameter types for each case.
It then show some ambiguity possible with this behavior :
class C {
int size() { return 0; }
static int size(Object arg) { return 0; }
void test() {
Fun<C, Integer> f1 = C::size;
// Error: instance method size()
// or static method size(Object)?
}
}
I've been using lambdas and method references in Java 8 for a while and there is this one thing I do not understand. Here is the example code:
Set<Integer> first = Collections.singleton(1);
Set<Integer> second = Collections.singleton(2);
Set<Integer> third = Collections.singleton(3);
Stream.of(first, second, third)
.flatMap(Collection::stream)
.map(String::valueOf)
.forEach(System.out::println);
Stream.of(first, second, third)
.flatMap(Set::stream)
.map(String::valueOf)
.forEach(System.out::println);
The two stream pipelines do the same thing, they print out the three numbers, one per line. The difference is in their second line, it seems you can simply replace the class name in the inheritance hierarchy as long as it has the method (the Collection interface has the default method "stream", which is not redefined in the Set interface).
I tried out what happens if the method is redefined again and again, using these classes:
private static class CustomHashSet<E> extends HashSet<E> {
#Override
public Stream<E> stream() {
System.out.println("Changed method!");
return StreamSupport.stream(spliterator(), false);
}
}
private static class CustomCustomHashSet<E> extends CustomHashSet<E> {
#Override
public Stream<E> stream() {
System.out.println("Changed method again!");
return StreamSupport.stream(spliterator(), false);
}
}
After changing the first, second and third assignments to use these classes I could replace the method references (CustomCustomHashSet::stream) and not surprisingly they did print out the debugging messages in all cases, even when I used Collection::stream. It seems you cannot call the super, overriden method with method references.
Is there any runtime difference? What is the better practice, refer to the top level interface/class or use the concrete, known type (Set)?
Thanks!
Edit:
Just to be clear, I know about inheritance and LSP, my confusion is related to the design of the method references in Java 8. My first thought was that changing the class in a method reference would change the behavior, that it would invoke the super method from the chosen class, but as the tests showed, it makes no difference. Changing the created instance types does change the behavior.
Even method references have to respect to OOP principle of method overriding. Otherwise, code like
public static List<String> stringify(List<?> o) {
return o.stream().map(Object::toString).collect(Collectors.toList());
}
would not work as expected.
As to which class name to use for the method reference: I prefer to use the most general class or interface that declares the method.
The reason is this: you write your method to process a collection of Set. Later on you see that your method might also be useful for a collection of Collection, so you change your method signature accordingly. Now if your code within the method always references Set method, you will have to adjust these method references too:
From
public static <T> void test(Collection<Set<T>> data) {
data.stream().flatMap(Set::stream).forEach(e -> System.out.println(e));
}
to
public static <T> void test(Collection<Collection<T>> data) {
data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
you need to change the method body too, whereas if you had written your method as
public static <T> void test(Collection<Set<T>> data) {
data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
you will not have to change the method body.
A Set is a Collection. Collection has a stream() method, so Set has that same method too, as do all Set implementations (eg HashSet, TreeSet, etc).
Identifying the method as belonging to any particular supertype makes no difference, as it will always resolve to the actual method declared by the implementation of the object at runtime.
See the Liskov Substitution Principle:
if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program
Am having some arguments say (String a, Treeset b, Set c)
and I try to get the class by arguments[i].getClass(); of the above arguments..
is Iit possible to get the class of the interface <Set>.
For example:
Class[] argumentTypes = new Class [arguments.length];
for (int i = 0 ; i < arguments.length ; i++)
{
argumentTypes[i] = arguments[i].getClass();
}
The code you've given will find the classes of the arguments (i.e. the values provided to the method) - but those can never be interfaces; they'll always be concrete implementations. (You can never pass "just a set" - always a reference to an object which is an instance of an implementation of the interface, or a null reference.)
It sounds like you want the types of the parameters - which you'd get via reflection if you absolutely had to, finding the Method and then getting the parameters from that with getParameterTypes. But given that you're within the method, you already know the parameter types, because they're at the top of the method... I'm not sure the best way of finding "the currently executing" method, if that's what you're after.
If you're just trying to get the class associated with Set, you can use Set.class of course. But again, it's not really clear what you're trying to do.
EDIT: Okay, judging from your comment, there are some logical problems with what you're trying to do. Going from the values of arguments to which method would be invoked is impossible in the general case, because you've lost information. Consider this, for example:
void foo(String x) {}
void foo(Object y) {}
foo("hello"); // Calls first method
foo((Object) "hello"); // Calls second method
Here the argument values are the same - but the expressions have a different type.
You can find all methods which would be valid for the argument values - modulo generic information lost by type erasure - using Class.isAssignableFrom. Does that help you enough?
Note that you'll also need to think carefully about how you handle null argument values, which would obviously be valid for any reference type parameter...
You can use http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#getInterfaces()
You will get the class what the caller provided.
I mean,in below class you will get HashSet.
Set set=new HashSet();
System.out.println(set.getClass());
You can do this in two ways given below
Set s = new //any class that implements it for example HashSet or TreeSet etc.;
s.getClass().getName(); //This will return the name of the subclass which is refered by s.
or if in other way can do it
Set s = null;
s.getClass();//This causes NullPointer Exception
in java, is it possible to access the instance to which a method belongs, given only the method?
for example:
public class ClassA {
private ClassB instanceB = new ClassB();
// ...
private void sendMethod () {
instanceB.receiveMethod(foo);
}
public void foo () {}
}
public class ClassB {
public void receiveMethod (Method method) {
Object o = foo.getInstanceOwner(); // just made that part up...
}
}
my feeling is that methods belong to classes, not instances of a class, so the answer is no, but maybe there's some sneaky reflection technique i don't know about. i could always pass 'this' along with method foo, but that seems like extra baggage.
Taken from
A Method provides information about, and access to, a single method on a class or interface. The reflected method may be a class method or an instance method (including an abstract method).
A Method permits widening conversions to occur when matching the actual parameters to invoke with the underlying method's formal parameters, but it throws an IllegalArgumentException if a narrowing conversion would occur.
You can call Method#invoke but you will need the instance of the object you want to call the method on, from the method doc:
Invokes the underlying method
represented by this Method object, on
the specified object with the
specified parameters. Individual
parameters are automatically unwrapped
to match primitive formal parameters,
and both primitive and reference
parameters are subject to method
invocation conversions as necessary.
If the underlying method is static,
then the specified obj argument is
ignored. It may be null.
If the number of formal parameters
required by the underlying method is
0, the supplied args array may be of
length 0 or null.
If the underlying method is an
instance method, it is invoked using
dynamic method lookup as documented in
The Java Language Specification,
Second Edition, section 15.12.4.4; in
particular, overriding based on the
runtime type of the target object will
occur.
If the underlying method is static,
the class that declared the method is
initialized if it has not already been
initialized.
If the method completes normally, the
value it returns is returned to the
caller of invoke; if the value has a
primitive type, it is first
appropriately wrapped in an object.
However, if the value has the type of
an array of a primitive type, the
elements of the array are not wrapped
in objects; in other words, an array
of primitive type is returned. If the
underlying method return type is void,
the invocation returns null.
So the TL:DR is unless you have the actual object you want you call the method on, it is not possible.
public class ClassA {
private ClassB instanceB = new ClassB();
// ...
private void sendMethod () {
Method m = ClassA.class.getMethod("foo", null);
instanceB.receiveMethod(m);
}
public void foo () {}
}
public class ClassB {
public void receiveMethod (Method method) {
Class c = method.getDeclaringClass();
}
}
gives you the owning Class. An instance doesn't own methods.
You can do this, but the proper way in your example would be the use of an interface, because that seems to be what you want: You want to pass in an object that ClassB knows how to operate on.
interface Callback {
void foo();
}
public class ClassA implements Callback {...}
public class ClassB {
public void receiveMethod(Callback cb) {...}
}
This is like asking:
"Given an apple from an Apple orchard, which tree owns this apple?"
The answer to which is:
"No idea, since all apple trees produce apples, it could belong to any tree".
... in other words - you must supply an instance from which the method will be called
EDIT
From one of your comments, I gather you are looking for an alternative of the Observer pattern. You say you don't like the messiness of the Observer pattern and that it is not "generic" enough for you.
I would argue that it is probably one of the least messiest patterns in existence, AND interfaces are by definition as generic as things get!
So, perhaps its an implementation problem you're having. Luckily, I have already posted on SO an Observer implementation in JAVA, to demonstrate how powerful and elegant it is.
Polymorphism and Interfaces in Java (can polymorphism be used to implement interfaces...why?)
In fact: reflection is messier than using an interface, since you can't guarantee at compile time that the type of Object you are invoking an instance of a Method on, even supports that method! (without some error checking code). Versus with interfaces, its not possible to even have that problem.