Limitations of forEach with instance method references in Java 8 - java

Assume I have the following functional interface:
public interface TemperatureObserver {
void react(BigDecimal t);
}
and then in another class an already filled-in ArrayList of objects of type TemperatureObserver.
Assuming that temp is a BigDecimal, I can invoke react in a loop using:
observers.forEach(item -> item.react(temp));
My question: can I use a method reference for the code above?
The following does not work:
observers.forEach(TemperatureObserver::react);
The error message is telling me that
forEach in the Arraylist observers is not applicable to the type TemperatureObserver::react
TemperatureObserver does not define a method react(TemperatureObserver)
Fair enough, as forEach expects as an argument a Consumer<? super TemperatureObserver>, and my interface, although functional, does not comply to Consumer because of the different argument of react (a BigDecimal in my case).
So can this be solved, or it is a case in which a lambda does not have a corresponding method reference?

There are three kinds of method references that can be used when a single value is available from the stream:
A parameter-less method of the streamed object.
class Observer {
public void act() {
// code here
}
}
observers.forEach(Observer::act);
observers.forEach(obs -> obs.act()); // equivalent lambda
The streamed object becomes the this object of the method.
A static method with the streamed object as parameter.
class Other {
public static void act(Observer o) {
// code here
}
}
observers.forEach(Other::act);
observers.forEach(obs -> Other.act(obs)); // equivalent lambda
A non-static method with the streamed object as parameter.
class Other {
void act(Observer o);
}
Other other = new Other();
observers.forEach(other::act);
observers.forEach(obs -> other.act(obs)); // equivalent lambda
There is also a constructor reference, but that is not really relevant to this question.
Since you have an external value temp, and you want to use a method reference, you can do the third option:
class Temp {
private final BigDecimal temp;
public Temp(BigDecimal temp) {
this.temp = temp;
}
public void apply(TemperatureObserver observer) {
observer.react(this.temp);
}
}
Temp tempObj = new Temp(temp);
observers.forEach(tempObj::apply);

Take a look at the Method References section in the Java Tutorial. There it says:
There are four kinds of method references:
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
There it explains that i.e. TemperatureObserver::react would be a method reference of the 3rd type: a reference to an instance method of an arbitrary object of a particular type. In the context of your call to the Stream.forEach method, that method reference would be equivalent to the following lambda expression:
(TemperatureObserver item) -> item.react()
Or just:
item -> item.react()
Which doesn't match your void TemperatureObserver.react(BigDecimal t) method signature.
As you already suspect, there are cases for which you can't find an equivalent method reference for a lambda. Lambdas are way more flexible, though IMHO sometimes they are less readable than method references (but this is a matter of taste, many people think the other way round).
A way to still use a method reference would be with a helper method:
public static <T, U> Consumer<? super T> consumingParam(
BiConsumer<? super T, ? super U> biConsumer,
U param) {
return t -> biConsumer.accept(t, param);
}
Which you could use as follows:
observers.forEach(consumingParam(TemperatureObserver::react, temp));
But, honestly, I prefer to use a lambda.

It does not works, because you iterate over handlers, not over parameters.
For example, this code works:
ArrayList<BigDecimal> temps = new ArrayList<>();
TemperatureObserver observer = new TemperatureObserverImpl();
temps.forEach(observer::react);

Related

Method accepts list of different objects in Java

Is it possible to do this in Java:
pass a parameter to a method which will accept list of different types
as well as use that Class type inside the method ?
public class Test {
public boolean testing(List<ClassType> testObjs) {
int vals = 0;
if (CollectionUtils.isNotEmpty(testObjs)) {
vals += testObjs.stream()
.map(ClassType::getTestType)
.filter(isTestobj::isTest)
.count();
}
do_processing_with_vals
return boolean_value;
}
}
There are 3 different classes which have method getTestType but unfortunately they are in different libraries and do not share a common parent class/Interface. I have to call a method (eg testing) which could take a list of either of these 3 class objects and do some processing with it. I do not want 3 different methods with only the difference being the class of the object that is passed (eg. ClassType).
When I use Object in place of ClassType, it throws error saying cannot resolve method getTestType.
Since the methods don't share a common interface (which is the best solution), the cleanest option may be to pass an appropriate Function or Predicate:
public boolean <T> testing(List<T> testObjs, Predicate<? super T> isTest) {
vals += testObjs.stream().filter(isTest).count();
}
...
testing(someObjs, ObjType::isTest);

I think that the Stream.filter() is not showing compile time error even with an invalid predicate

In the below code I have this line: stream.filter(Data::isEven);
I am using a filter() and a filter() accepts a Predicate interface as parameter. We all know that Predicate has one method with the signature: public boolean test(T t);
which accepts one parameter and returns a boolean.
I am of the understanding that the isEven() method that accepts no parameter is not a valid Predicate because unlike test() method it does not take any parameter, so how come my code is not showing a compile time error?
import java.util.stream.Stream;
public class Main App {
public static void main(String args[]) {
Stream<Data> stream =
Stream.of(new Data(4), new Data(1));
stream.filter(Data::isEven); // should throw compile error but doesn't
}
}
class Data{
int i;
public Data(int i) {
this.i=i;
}
public boolean isEven() {
return i%2==0;
}
}
The thing is that Data::isEven is a method reference equivalent to data -> data.isEven() predicate :
Predicate<Data> predicate = data -> data.isEven();
// is the same as
Predicate<Data> predicate= Data::isEven;
This is described in JLS 15.13 :
The target reference of an instance method (§15.12.4.1) may be provided by the method reference expression using an ExpressionName, a Primary, or super, or it may be provided later when the method is invoked.
....
Evaluation of a method reference expression produces an instance of a functional interface type (§9.8). Method reference evaluation does not cause the execution of the corresponding method; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.
In your case Data::isEven is a reference to instance method isEven of Data object.
Data::isEven is a Predicate.
To call this method you have to pass value, like: myData.isEven().
This is the same as it would be isEven(myData). So the difference is only in syntax (parameter either before a dot, or inside parenthesis), but semantically it is the same.
Therefore isEven is a Predicate<Data> because it accepts Data and returns Boolean.
As others wrote "Data::isEven" or "data -> data.isEven()" is Predicate here. When we invoke test method of this predicate, we pass the instance of data (you have a stream of such instances) there as a parameter.

Passing generic ToIntFunction to method in one line

Suppose I have a several Lists of different objects and I want to map these Objects to an int value (Such as returning the length of a String) so I can perform operations in a Stream. So I create a method such as:
public <T> int foo(List<T> list, ToIntFunction<T> toInt) {
return list.stream().mapToInt(toInt).max().orElse(Integer.MIN_VALUE);
}
(Note: max may be some other terminal operation)
Then I want to pass a ToIntFunction to it. I can do:
ToIntFunction<String> length = e -> e.length();
int bar = foo(list, length);
However I would have to write out the ToIntFunction every time I called the method.
Is there a way to just do something like:
foo(list, new ToIntFunction<String>(e -> e.length()) );
//Obviously ToIntFunction is an interface and can't be instantiated
Or is it necessary to do it on two lines with a named variable?
Just pass it as an inline lambda like so,
foo(Arrays.asList("test"), e -> e.length());
Or else here's the more readable method reference based approach,
foo(Arrays.asList("test"), String::length);
The trick here is that the ToIntFunction<T> is a single abstract method interface and the language allows you to create instances of these interfaces using lambda expressions.
Above lambda expression is just a syntactic sugar which substitutes more verbose anonymous inner class. Here's that pre-Java8 approach.
int bar = foo(Arrays.asList("test"), new ToIntFunction<String>() {
#Override
public int applyAsInt(String value) {
return value.length();
}
});

Java Method reference not expected here

How exactly do you chain method references for instances with Java 8? Example:
Collections.sort(civs,Comparator.comparing(Civilization::getStrategy.getStrategLevel));
getStrategy of a Civilization instance returns a Strategy object instance which has the instance method getStrategyLevel.
Why doesn't the Comparator.comparing method return a comparator with it's functional interface implemented by the lambda expression?
In that case, you should use a lambda, you can't apply a method reference directly:
Collections.sort(civs, Collectors.comparing(c -> c.getStrategy().getStrategLevel()));
Though, there is a way to use a method reference here. Assuming that you have a class like
class CivilizationUtils {
public static Integer getKeyExtractor(Civilization c) {
return c.getStrategy().getStrategLevel();
}
}
the issue could be solved like
Collections.sort(civs, Collectors.comparing(CivilizationUtils::getKeyExtractor));
You cannot do it with a method reference, you need to use a lambda expression or create a static method.
There are four kinds of method references:
Reference to a static method like ContainingClass::staticMethodName
Reference to an instance method of a particular object like containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type like ContainingType::methodName
Reference to a constructor like ClassName::new
More details about method reference.
So here, with a lambda expression it would be:
Collections.sort(civs, Comparator.comparing(c -> c.getStrategy.getStrategLevel()));
Or in case you create a static method
public static int getStrategLevel(Civilization c) {
return c.getStrategy().getStrategLevel();
}
Then your code would be:
Collections.sort(civs, Comparator.comparing(MyClass::getStrategLevel));
Collections.sort(civs,Comparator.comparing(civ -> civ.getStrategy().getStrategLevel()));
Different example, but I have a method
void m(Predicate<String> stringPredicate)
and a utility class
class Utilities {
static boolean condition1(String s) { ... }
static boolean condition2(String s) { ... }
...
}
and I wanted to invoke m with a predicate that returns true iff Utilities.condition1 returns false. The Java grammar allows me to write
m(Utilities::condition1)
but not
m(Utilities::condition1.negate())
(an unfortunate violation of referential transparency), and the compiler complained, "Java Method reference not expected here."
My workaround was to write a method
Predicate<String> not(Predicate<String> p) {
return p;
}
and then to write the call
m(not(Utilities::condition1))
--which is allowed by the Java grammar.

How to specify function types for void (not Void) methods in Java8?

I'm playing around with Java 8 to find out how functions as first class citizens. I have the following snippet:
package test;
import java.util.*;
import java.util.function.*;
public class Test {
public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
list.forEach(functionToBlock(myFunction));
}
public static void displayInt(Integer i) {
System.out.println(i);
}
public static void main(String[] args) {
List<Integer> theList = new ArrayList<>();
theList.add(1);
theList.add(2);
theList.add(3);
theList.add(4);
theList.add(5);
theList.add(6);
myForEach(theList, Test::displayInt);
}
}
What I'm trying to do is pass method displayInt to method myForEach using a method reference. To compiler produces the following error:
src/test/Test.java:9: error: cannot find symbol
list.forEach(functionToBlock(myFunction));
^
symbol: method functionToBlock(Function<Integer,Void>)
location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
myForEach(theList, Test::displayInt);
^
required: List<Integer>,Function<Integer,Void>
found: List<Integer>,Test::displayInt
reason: argument mismatch; bad return type in method reference
void cannot be converted to Void
The compiler complains that void cannot be converted to Void. I don't know how to specify the type of the function interface in the signature of myForEach such that the code compiles. I know I could simply change the return type of displayInt to Void and then return null. However, there may be situations where it's not possible to alter the method I want to pass somewhere else. Is there an easy way to reuse displayInt as it is?
You are trying to use the wrong interface type. The type Function is not appropriate in this case because it receives a parameter and has a return value. Instead you should use Consumer (formerly known as Block)
The Function type is declared as
interface Function<T,R> {
R apply(T t);
}
However, the Consumer type is compatible with that you are looking for:
interface Consumer<T> {
void accept(T t);
}
As such, Consumer is compatible with methods that receive a T and return nothing (void). And this is what you want.
For instance, if I wanted to display all element in a list I could simply create a consumer for that with a lambda expression:
List<String> allJedi = asList("Luke","Obiwan","Quigon");
allJedi.forEach( jedi -> System.out.println(jedi) );
You can see above that in this case, the lambda expression receives a parameter and has no return value.
Now, if I wanted to use a method reference instead of a lambda expression to create a consume of this type, then I need a method that receives a String and returns void, right?.
I could use different types of method references, but in this case let's take advantage of an object method reference by using the println method in the System.out object, like this:
Consumer<String> block = System.out::println
Or I could simply do
allJedi.forEach(System.out::println);
The println method is appropriate because it receives a value and has a return type void, just like the accept method in Consumer.
So, in your code, you need to change your method signature to somewhat like:
public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
list.forEach(myBlock);
}
And then you should be able to create a consumer, using a static method reference, in your case by doing:
myForEach(theList, Test::displayInt);
Ultimately, you could even get rid of your myForEach method altogether and simply do:
theList.forEach(Test::displayInt);
About Functions as First Class Citizens
All been said, the truth is that Java 8 will not have functions as first-class citizens since a structural function type will not be added to the language. Java will simply offer an alternative way to create implementations of functional interfaces out of lambda expressions and method references. Ultimately lambda expressions and method references will be bound to object references, therefore all we have is objects as first-class citizens. The important thing is the functionality is there since we can pass objects as parameters, bound them to variable references and return them as values from other methods, then they pretty much serve a similar purpose.
When you need to accept a function as argument which takes no arguments and returns no result (void), in my opinion it is still best to have something like
public interface Thunk { void apply(); }
somewhere in your code. In my functional programming courses the word 'thunk' was used to describe such functions. Why it isn't in java.util.function is beyond my comprehension.
In other cases I find that even when java.util.function does have something that matches the signature I want - it still doesn't always feel right when the naming of the interface doesn't match the use of the function in my code. I guess it's a similar point that is made elsewhere here regarding 'Runnable' - which is a term associated with the Thread class - so while it may have he signature I need, it is still likely to confuse the reader.
Set return type to Void instead of void and return null
// Modify existing method
public static Void displayInt(Integer i) {
System.out.println(i);
return null;
}
OR
// Or use Lambda
myForEach(theList, i -> {System.out.println(i);return null;});

Categories