Can anybody explain this how this line of code works:
Rational sum = a.add(b).add(c);
I don't understand how object b (which is an argument) is receiving a method?
This is called method chaining. The method add() actually returns a reference of the currently modified object or a new object of the same type on which the method was invoked. Say suppose the object referred to by a is a BigInteger , when you invoke a.add(b) , it returns a BigInteger object whose value is a+b , and hence you can invoke .add(c) on that object again.
Rational sum = a.add(b).add(c);
// is equivalent to
Rational temp = a.add(b);
Rational sum = temp.add(c);
Method chaining is not required. It only potentially improves readability and reduces the amount of source code. It is the core concept behind building a fluent interface.
A sample illustration:
This practice is used mostly in Builder pattern, you can find this pattern in API itself in StringBuilder class.
I don't understand how object b (which is an argument) is receiving a method?
No your understanding is wrong , a.add(b) means you are invoking method add() on object a and passing it a reference of object b . The resultant object which the method a.add(b) returns is of the same type as a , and then in succession you call the method .add(c) on the returned object passing a reference of object c to that method.
Its fluent chaining
Each method in the chain has to return a class or an interface. The next method in the chain has to be a part of the returned class.
in your case a.add(b) returning some calss/interface and then calling add(c) on that and that method returns your sum
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;
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).
Is it possible to run a method, in a consumer, like a method reference, but on the object passed to the consumer:
Arrays.stream(log.getHandlers()).forEach(h -> h.close());
would be a thing like:
Arrays.stream(log.getHandlers()).forEach(this::close);
but that's not working...
Is there a possibility with method references, or is x -> x.method() the only way working here?
You don't need this. YourClassName::close will call the close method on the object passed to the consumer :
Arrays.stream(log.getHandlers()).forEach(YourClassName::close);
There are four kinds of method references (Source):
Kind Example
---- -------
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new
In your case, you need the third kind.
I suppose it should be:
Arrays.stream(log.getHandlers()).forEach(Handler::close);
Provided the log.getHandlers() returns an array of objects of type Handler.
Sure, but you must use the correct syntax of method reference, i.e. pass the class to which the close() method belong:
Arrays.stream(log.getHandlers()).forEach(Handler::close);
When a method returns an object, does that method automatically create a new instance of
that object in memory?
For example:
In Java BigInteger class , I use the add method where num1, and num2, lets say, are BigIntegers.
num1.add(num2);
Then I reference it by the same type as in:
BigInteger a = num1.add(num2);
This works and gets the correct data. So the method will create a new instance of that object on the stack?
Just making sure I'm correct about my assumption.
Thanks.
bigInteger.add() does return a new instance, but not all methods do.
What usually returns new object instances that you may run into regularly are:
Constructors
Factorys/Builders
methods operating on immutable objects
BigInteger's add method says that it returns a BigInteger, doesn't say a new BigInteger, but given that the docs also state that BigInteger is immutable, then you can know it's not returning the this object with a modification, so chances are that it's a new object (I suppose it's possible to be some cached object already representing that state, but even if it was, your usage of BigInteger wouldn't change.).
It depends.
With your BigInteger example, it is returning a new instance of the object.
However with something like StringBuilder, you can do
stringBuilder.append("this").append("is").append("a").append("string");
and each .append(String) is returning the same original object, it just allows you to chain the calls together.