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
Related
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.).
Given the following code:
static <U,V> List<V> map(Iterable<U> l, Function<U,V> f) {
return null;
}
I need to pass an arraylist in the first parameter and a hash function in the second which takes a string and outputs a type int.
I'm trying the following but get the following error message.
map(names,<String,Integer> hashCode());
map(names,hash());
Error message:Not applicable for the arguments int
Need to pass a hash function so I can use this function inside the method.
Here:
hashCode()
isn't a function! It is simply a method invocation. You would rather need a lambda, something like:
x -> x.hashCode()
or, as you are probably using the hashcode of the current object:
this::hashCode
But that actually depends on the object on which you intend to invoke the hashCode method. And of course, there is also difference regarding the interface you intend to "use", be it a Function or Supplier (that second method reference example would be a supplier for example).
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.
In the code below, I've written two methods:
One that has the variable test defined as a String
One that returns two different outcomes depending on what is typed
I have the variable named test in userInputhere while the parameter in Hello is named message. In userInputhere I use test instead of message - why does this work?
Does the parameter not matter when you invoke the Hello method?
I get that when entering a method that returns something, it has to be defined and the parameters define further what the method is going to work on, so, I had assumed that, when calling said method from another method, you would have to use the same parameters, but that doesn't seem to be the case.
import java.util.Scanner;
public class methodsandparameters {
static Scanner input = new Scanner(System.in);
public static void main(String[] args){
userInputhere();
}
public static void userInputhere(){
String test = input.nextLine();
System.out.println(Hello(test));
}
public static String Hello(String message){
if (message.equalsIgnoreCase("Hi")) {
return "Hello";
} else {
return "Goodbye";
}
}
}
When you invoke the Hello method, its parameter (message) is contained inside of the Hello method itself. You are allowed to pass any argument to Hello as long as it is a string (since message is of type String). It does not matter what you pass to Hello as long as it resolves to a String. So in your example, the variable test works fine because it is a String.
The program is not actually invoking "test" or invoking "message", it is only invoking the Hello method which can take any String as an argument.
The reason the parameter names, as you call them, don't match is only one is a parameter. When you call a function you pass it a number of arguments that must match the type and number of parameters a function takes. So if you have a function add(int x, int y) it doesn't matter how you call it as long as both arguments are ints, they don't have to be named x and y when you call the function.
So a function/method takes parameters and calling a function passes arguments.
The name of a variable only matters within it's scope (i.e. where you can reference it)[1].
For a method parameter, that's only in the body of that method.
Outside of that method, the only thing that matters is the type - String, in this case. As long as you pass it something of the same type, or a type that can be automatically converted to the given type, it's happy - whatever you called the parameter (message, in this case) will be given the value of whatever you passed to your function in that position in the arguments and each time you use that parameter name in the method, you'll be using that value that's been given to it (at least until you reassign it in your method, by saying message = ...).
You can also do things like Hello(input.nextLine()) or Hello("Hi") - no need to use a temporary variable.
[1]: Until you start talking about reflection (but no need to worry about that yet).
I have a class
class Configuration {
// various stuff
#Override
public String toString() {
// assemble outString
return outString;
}
}
I also have another class
class Log {
public static void d(String format, Object... d) {
// print the format using d
}
}
The Log class works perfectly fine, I use it all the time. Now when I do this:
Configuration config = getConfiguration();
Log.d(config);
I get the compiler error The method d(String, Object...) in the type Log is not applicable for the arguments (Configuration). I can solve this:
Log.d("" + config); // solution 1
Log.d(config.toString()); // solution 2
My problem: How is this different? In the first solution, the compiler notices that it has to concatenate two Strings, but the second one is a Configuration. So Configuration#toString() is called and everything is fine. In the compiler error case the compiler sees that a String is needed, but a Configuration is given. Basically the same problem.
Needed: String
Given: Configuration
How are these cases different and why is toString not called?
While designing the language, someone decided that when a programmer appends an arbitrary object to a string using the + operator, they definitely want a String, so implicitly calling toString() makes sense.
But if you call an arbitrary method that takes a String with something else, that is simply a type error, exactly what all that static typing is supposed to prevent.
The line
"" + config
gets translated to something like
StringBuilder sb = new StringBuilder("");
sb.append(config);
where the second line calls
StringBuilder.append(Object obj);
This method calls obj.toString() to get the String representation of the object.
On the other hand, the first parameter of Log.d must be a String, and Java doesn't automatically call toString() to cast everything to a String in that case.
One of the common use of toString(), is print() and println() methods of PrintStream, as in:
System.out.print(object);
System.out.println(object);
Basically, these two methods will call toString() on the passed object. This is one of the Polymorphism's benefits.
Nice Question...
But,
Compiler does not call a method to match formal parameters. it simply tries to cast the objects if possible.
But when you use the "+" operator the compiler executes the toString() method of its arguments (in case they are objects) by default.
In one case you are passing an object argument to an operator which expects objects.
In the earlier case you are passing an object argument to a function which expects string.
Basically function/operator signature is different.
It is almost incidental [in the context of this question] that .tostring called when + is applied. It takes an object and does something.
For all you know, you might be passing in object when string is required by mistake. So it can't blindly do .tostring()
You are passing Configuration class object argument in case 1 but in the case 2 , you are passing string argument . so no error occures.