How does method reference casting work? - java

public class Main {
interface Capitalizer {
public String capitalize(String name);
}
public String toUpperCase() {
return "ALLCAPS";
}
public static void main(String[] args) {
Capitalizer c = String::toUpperCase; //This works
c = Main::toUpperCase; //Compile error
}
}
Both are instance methods with same signature. Why does one work and the other doesn't?
Signature of String::toUpperCase: String toUpperCase();

There are 3 constructs to reference a method:
object::instanceMethod
Class::staticMethod
Class::instanceMethod
The line:
Capitalizer c = String::toUpperCase; //This works
use 3'rd construct - Class::instanceMethod. In this case first parameter becomes the target of the method. This construct is equivalent (translates) to following Lambda:
Capitalizer = (String x) -> x.toUpperCase();
This Lambda expression works because Lambda gets String as parameter and returns String result - as required by Capitalizer interface.
The line:
c = Main::toUpperCase; //Compile error
Translates to:
(Main m) -> m.toUpperCase();
Which does not work with the Capitalizer interface. You could verify this by changing Capitalizer to:
interface Capitalizer {
public String capitalize(Main name);
}
After this change Main::toUpperCase will compile.

You have a method which
public String capitalize(String name);
Takes a String and returns a String. Such a method can have a number of patterns.
A constructor
c = String::new; // calls new String(String)
// or
c = s -> new String(s);
A function on String which takes no arguments
c = String::toLowerCase; // instance method String::toLowerCase()
// or
c = s -> s.toLowerCase();
of a method which takes a String as the only argument
// method which takes a String, but not a Main
public static String toUpperCase(String str) {
c = Main::toUpperCase;
// or
c = s -> toUpperCase(s);
In every case, the method referenced has to take the String.
If not you can do this instead.
c = s -> capitalize(); // assuming Main.capitalize() is static
This tells the compiler to ignore the input.

You should change:
public String toUpperCase()
to
public static String toUpperCase(String text)
You should read the java tutorial on method references. The different kind of method references and there is a similar example with String::compareToIgnoreCase (Reference to an Instance Method of an Arbitrary Object of a Particular Type).
The equivalent lambda expression for the method reference String::compareToIgnoreCase would have the formal parameter list (String a, String b), where a and b are arbitrary names used to better describe this example. The method reference would invoke the method a.compareToIgnoreCase(b).

Related

Java Functional Interface with one argument can apply to no argument method?

References
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/function/package-summary.html
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/String.html#isEmpty()
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/function/ToIntFunction.html
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Integer.html#intValue()
This is Predicate Functional Interface's abstract method. It gets one argument T t and return boolean.
boolean test​(T t)
Evaluates this predicate on the given argument.
But, in this example, String::isEmpty does not get any argument but it is instance method of String.
Predicate<String> p = String::isEmpty;
I can infer that in Java, this type of method can apply to Functional Interface with one argument.
And, is there any examples of one argument Functional Interface with the function has one argument not like upper example?
There are three forms that a Predicate can take. Here they are for isEmpty on a String:
1- A formal functional interface:
Predicate<String> somePredicate = new Predicate<String>() {
#Override
public boolean test(String s) {
return s.isEmpty();
}
};
2- A lambda:
(String s) -> s.isEmpty()
3- A method reference
String::isEmpty
These have identical function and can each be passed when a Predicate<String> is required.
The third doesn't apply if the underlying function is not a method on an object.
You can do Function<T, R> where R is the return type and T is the argument type.
So R result = fnc.apply(k) where k is of type T.
Function<Integer, List<Integer>> fnc = n-> new ArrayList<>(List.of(n));
List<Integer> list = fnc.apply(23);
System.out.println(list);
prints
[23]
But you can also do this, just like in your example.
Function<Integer, List<Integer>> fnc2 = fnc::apply;
fnc2.apply(44); // returns a list which contains 44
EXTRA DETAILS
Consider the following class. One static and one instance method.
class TestClass {
public int prod(int a) {
return a * 33;
}
public static int mult(int a) {
return a * 44;
}
}
To create a Lambda for the static, do the following:
// use unaryOperator since return type are argument are the same
UnaryOperator<Integer> fnc = TestClass::mult;
int result = fnc.apply(10); // returns 440
Now do an instance version.
UnaryOperator<Integer> inst = tc::prod;
inst.apply(10);
result = inst.apply(10); // returns 330
Now a special case using a static type reference.
to an instance method.
// This requires 2 arguments. The instance
// and the argument to the method.a
BiFunction<TestClass, Integer, Integer> stat = TestClass::prod
result = stat.apply(tc, 12); // result is 396

Java 8 Method references default method implementation

interface HelloWorld {
String hello(String s);
}
public static void main(String[] args) {
HelloWorld h = String::new;
System.out.println(h.hello("dasdasdadasd"));
}
When I execute the above method,it returns the value which I am passing in the arguments dasdasdadasd
Which method of string class is executed or is there any default implementation which java provides at runtime or by default it calls supplier.get() method?
h was assigned a method reference to the public String(String original) constructor of the String class. That's the only constructor that matches the signature of the String hello(String s) method of your HelloWorld interface.
Therefore h.hello("dasdasdadasd") creates a new String instance whose value is equal to "dasdasdadasd" and returns that instance.
HelloWorld h = String::new;
is equivalent to:
HelloWorld h = s -> new String(s);
Your code can be rewritten as:
hw returns a string created from what it receives:
//Returns a string based on the input
HelloWorld hw = (s) -> {
return new String(s);
};
Invoking hello() on that object returns "basically" the input:
//The value assigned to print is "dasdasdadasd", as returned by hw
String print = hw.hello("dasdasdadasd");
println Is receiving dasdasdadasd:
System.out.println(print); //"dasdasdadasd" is passed to println

Why does Java allow a compare(String, String) Comparator method to take arguments of Object type?

So here is my code:
public class Demo {
public static final Comparator<String> SORT_BY_LENGTH = new SortByLength();
private static class SortByLength implements Comparator<String>{
public int compare(String w, String v) {
return w.length()-v.length();
}
}
public static void main(String[] args) {
Object o1 = "abc", o2 = "bc";
Comparator c = SORT_BY_LENGTH;
System.out.println(c.compare(o1, o2));//OK, output 1
}
}
So what confuses me is that the signature of the compare() method takes 2 String variables as argument. However, even when I input 2 arguments of Object type, it still works. Why is that?
PS: if I define some ordinary method as follows, then the compiler will complain there is an error, because Object cannot be converted to String.
public static int foo(String w, String v) {
return w.length()-v.length();
}
public static void main(String[] args) {
Object o1 = "abc", o2 = "bc";
System.out.println(foo(o1, o2));// compiling error!
}
You can use Object as arguments here because you used the raw type of Comparator, which is something that you generally should not do.
Here is where you did it:
Comparator c = SORT_BY_LENGTH;
See how there are not generic arguments to Comparator? That is a sign of using raw types. You can assign any instance of a parameterised type, like SORT_BY_LENGTH to a raw type. However, since the raw type Comparator has no generic parameters, its compare method takes two Objects instead of Strings. So you can theoretically pass anything into compare.
However, if the wrong type of object goes into the method, an exception will be thrown. Try putting two new Object() in there!
This is why you should not assign parameterised types to raw types. They are not type safe. You should do something like this instead:
Comparator<String> c = SORT_BY_LENGTH;
or use SORT_BY_LENGTH directly.
If you made the above change, the call to compare will not compile anymore, because it is expecting Strings. You should change the type of o1 and o2 to String.

Why does this method reference assignment compile?

I'm struggling to see why the following code compiles:
public class MethodRefs {
public static void main(String[] args) {
Function<MethodRefs, String> f;
f = MethodRefs::getValueStatic;
f = MethodRefs::getValue;
}
public static String getValueStatic(MethodRefs smt) {
return smt.getValue();
}
public String getValue() {
return "4";
}
}
I can see why the first assignment is valid - getValueStatic obviously matches the specified Function type (it accepts a MethodRefs object and returns a String), but the second one baffles me - the getValue method accepts no arguments, so why is it still valid to assign it to f?
The second one
f = MethodRefs::getValue;
is the same as
f = (MethodRefs m) -> m.getValue();
For non-static methods there is always an implicit argument which is represented as this in the callee.
NOTE: The implementation is slightly different at the byte code level but it does the same thing.
Lets flesh it out a bit:
import java.util.function.Function;
public class MethodRefs {
public static void main(String[] args) {
Function<MethodRefs, String> f;
final MethodRefs ref = new MethodRefs();
f = MethodRefs::getValueStatic;
f.apply(ref);
//is equivalent to
MethodRefs.getValueStatic(ref);
f = MethodRefs::getValue;
f.apply(ref);
//is now equivalent to
ref.getValue();
}
public static String getValueStatic(MethodRefs smt) {
return smt.getValue();
}
public String getValue() {
return "4";
}
}
A non-static method essentially takes its this reference as a special kind of argument. Normally that argument is written in a special way (before the method name, instead of within the parentheses after it), but the concept is the same. The getValue method takes a MethodRefs object (its this) and returns a string, so it's compatible with the Function<MethodRefs, String> interface.
In the Java Tutorial it is explained that there are 4 different types of method references:
reference to a static method
reference to an instance method of a particular object
reference to an instance method of an arbitrary object of a particular type
reference to a constructor
Your case is #3, meaning that when you have an instance of MethodRef i.e. ref, calling apply on your function f will be equivalent to String s = ref.getValue().
For non-static methods, the type of this is considered implicitly to be the first argument type. Since it's of type MethodRefs, the types check out.

Java pass in string and return string

Would some one be able to help me please?
If I have a class like this
public class a {
public String b (String c){
String d = "e";
return d;
}
}
When I call a f = new a();
f.b();
I'm unable to have the string d returned. I get the error "cannot be applied to ()"
I'm sure I'm doing something stupid but I cant work it out.
You have
public String b (String c){
but call b() without any parameter. That's what the error wants to tell you...
method b takes a parameter. so try
f.b("some string c");
Your method b requires a String to be passed into it.
When you call f.b() it looks for a method with a signature similar to
public String b(){
// your code
}
You have to call like
f.b(aStringVariable);
or
f.b("Some String");
You have to pass the variable while calling the function.
Add ... to your method parameter declaration. This will make String parameter optional.
public String b (String... c){
String d = "e";
return d;
}
and then new a().b().
When you call f.b() you're not passing a string to the method. You declared your function as public String b(String c). That means you have to pass a string when you call f.b.
If you change you declaration to public String b() you do not have to pass a string.
Another solution is simply passing a string, i.e f.b("a string").
You are passing String c into your b method
public String b (String c)
you are getting that error because there needs to be a string variable like
a f = new a(); f.b(c);
Where c is some predefined string.
Use some string parameter and it will work: f.b("String")

Categories