This question already has answers here:
Reference to an instance method of a particular object
(6 answers)
Closed 4 years ago.
I've seen a lot of methods where a new class is instantiated in a lambda method reference but can't seem to understand why. When is the new keyword needed in a method reference?
For example, the following passes compilation:
UnaryOperator<String>stringToUpperCase = String::toUpperCase;
But this doesn't:
UnaryOperator<String>stringToUpperCase = new String()::toUpperCase;
String::toUpperCase is a method reference that can be applied to any String instance.
new String()::toUpperCase is a method reference that can be applied to a specific String instance (the instance created by new String()).
Since UnaryOperator<String> expects a method that takes a String and returns a String, String::toUpperCase fits (since you can apply it on a String and get the upper case version of that String).
On the other hand, new String()::toUpperCase doesn't fit UnaryOperator<String>, since it is executed on an already specified String, so you can't pass another String instance to it.
It can, however, by assigned to a Supplier<String>, since it simply supplies an empty String instance:
Supplier<String> emptyStringToUpperCase = new String()::toUpperCase;
This is similar to:
Supplier<String> emptyStringToUpperCase = () -> new String().toUpperCase();
while this:
UnaryOperator<String> stringToUpperCase = String::toUpperCase;
is similar to:
UnaryOperator<String> stringToUpperCase = s -> s.toUpperCase();
There are four kinds of method references as shown below and your type falls in the second category, but UnaryOperator<String> essentially needs to represent a method which accepts any String argument and returns a String. However, the non-working method reference that you have used is actually working on a particular String object (i.e. not any String object)
Refer: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
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
So I was reading generics and functional interfaces. There were two ways shown - Using Lambdas, Using method references. There were below examples used:
Predicate<String> ref = String::isEmpty;
Java uses the parameter supplied at runtime as the instance on which isEmpty is called. This is allowed because isEmpty() is an instance method in String class and doesn't take any parameter.
My question is, why does it shows compile error when I use the below line of code:
Supplier<Integer> ref2 = Random::nextInt;
After all, nextInt() is an instance method in Random class just like isEmpty() in String class and it doesn't take parameter either.
Random::nextInt is an instance method, so it needs an instance of Random in order to be called. Just like you can't call String::isEmpty without a String. That's why String::isEmpty is a match for Predicate<String>: it takes a String as an argument and returns a boolean.
Similarly, Random::nextInt needs an instance of Random as an argument, and returns an int. So it could be used as a Function<Random, Integer>; but not a Supplier<Integer>, because it cannot be called without arguments.
Alternatively, if you have an instance of Random, you can use a reference to the nextInt method of that particular instance as a Supplier.
Random random = new Random();
Supplier<Integer> randomIntSupplier = random::nextInt;
for example in Scanner we have
obj.next()
but we can call another method after next()
obj.next().charAt(0)
how can I make similar thing for example
obj.getName().toLowerCase()
What you have observed – with examples like obj.getName().toLowerCase() – is that when the return type of a method call is itself some other object, then you can immediately call a new method on that newly returned object.
Here's another example: String s = String.class.getName().toLowerCase();. This example could be rewritten like so:
Class<String> stringClass = String.class;
String name = stringClass.getName();
String s = name.toLowerCase();
Both of the one-line and multi-line version of this code result in a String object, referenced by s, which contains the value "java.lang.string".
Note that chaining method calls together is not possible if the return type isn't an object, such as an integer value. For example, here's a method call that results in a primitive long value, which isn't an object, so you can't call any methods on that result – that is, something like millis.hashCode() isn't possible.
long millis = System.currentTimeMillis();
To address your primary question finally: you can create this same behavior by creating methods that return objects instead of primitives (int, long, byte, etc.).
This question already has answers here:
What is polymorphism, what is it for, and how is it used?
(29 answers)
Closed 5 years ago.
Here is my example code:
String str = "hello";
Object obj = (Object)str;
System.out.println(obj.toString());
I found the source code of Object, and the toString() method is:
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
I thought the result of the example shound be the address of this Object, like [B#15db9742 , after I convert str to Object , but it still print hello. Why? Shoundn't obj use the method of Object? Can Anyone explain the principle of it to me?
This is polymorphism (specifically, runtime polymorphism). It doesn't matter what the type of your reference to the object is (Object or String), as long as that type has toString (so your code compiles), the toString that will be used is the one the object itself actually has, not necessarily the one provided by the type of your reference. In this case, the object is a String no matter what the type of your reference to it is, so String#toString is used.
Because the underlying object is a String object and the String class has overridden the toString() method in it.
Though you have the type Object on left hand, the methods from implemented Class gets execute and the overall phenomenon called as Polymorphism. You are Just changing the Form of String to Object.
When you do
Object obj = (Object)str;
That doesn't change String to Object class. You are just changing the type of the Object not the actual behaviour of it.
Virtual method invocation implies the method override will be executed given the actual implementation, as opposed to the implementation in the reference type.
In other words, your String instance will still invoke overrides of Object's methods implemented in String, even if cast as Object.
Object obj is the result of an (unnecessary) cast, but is still a String....
the String class has an implemented overide of toString method:
public String toString() {
return this;
}
verify that obj is a String by doing:
System.out.println(str.getClass());
System.out.println(obj.getClass());
you will get
class java.lang.String
class java.lang.String
This question already has answers here:
Which overload will get selected for null in Java?
(3 answers)
Closed 8 years ago.
I want to know whether this is a valid overloading :
public class OverLoadingTest{
private void callFunction(Object object){
System.out.println("Printing Object");
}
private void callFunction(String string){
System.out.println("Printing String");
}
}
Further more, since someone asked me this question.
If I do like this,
OverLoadingTest test = new OverLoadingTest();
test.callFunction(null);
what will be printed ?
Of course my opinion is that it isn't valid overloading at all.
So no question of the second part.
Please tell me about this with some explanation.
The method with the least generic argument is called. So, in your case, it will be method accepting String
Note : If 2 classes are at the same level, then you will get an ambiguous call exception. For example if one method took String and another took Exception.
If more than one member method is both accessible and applicable to a method
invocation, it is necessary to choose one to provide the descriptor for
the run-time method dispatch.
The Java programming language uses the rule that the most specific method is chosen.
See more details in JSL 15.12.2.5
In your case, String method will be invoked, if argument is String or null and for other argument's types Object method will be invoked.
In your example, if you define one more method with argument type that is not String (e.g Integer), can't compile the source as it is ambiguous to be invoked between the methods with String and Integer as they are same level.