Sometimes I see examples of using a diamond operator in the method invocation.
An example of syntax is:
.<ClassName>methodName()
I'm not sure that I understand entirely in which condition there is a case for such use. Didn't find an explanation of that in the references.
May you explain that case or suggest a source of information where I can find an explanation?
I am providing working an example down below. This example uses RxJava2.
There Flowable.<Integer>create(emitter -> emit(emitter), BackpressureStrategy.BUFFER)
doesn't compile without <Integer>
because the method emit accepts FlowableEmitter<Integer> emitter
.
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.schedulers.Schedulers;
public class Sample {
public static void main(String[] args) {
Flowable.<Integer>create(emitter -> emit(emitter), BackpressureStrategy.BUFFER)
.observeOn(Schedulers.computation(), true,2)
.map(data -> data * 1)
.subscribe(Sample::process,
err -> System.out.println("ERROR: " err),
() -> System.out.println("DONE"));
}
public static void process(int value) {
System.out.println(value);
sleep(1000);
}
private static void emit(FlowableEmitter<Integer> emitter) {
int count = 0;
while(count < 10) {
count ;
System.out.println("Emitting ..." count);
emitter.onNext(count);
sleep(500);
}
}
private static boolean sleep(int ms) {
try {
Thread.sleep(ms);
return true;
} catch (InterruptedException e) {
return false;
}
}
}
When I try to simulate a similar case with plain Java it doesn't work. What I understand wrong?
public class Sample2 {
public static void main(String[] args) {
GenericClass.<Integer>genericMethod(param -> processParam(param));
}
private static void processParam(GenericClass<Integer> b) {
}
}
}
class GenericClass <T> {
public static <T> GenericClass<T> genericMethod(GenericInterface<T> genericinterface) {
return new GenericClass<T>();
}
}
interface GenericInterface <T> {
T doJob();
}
CodePudding user response:
ClassName.<Type>methodName(...)
is one of the forms of method invocations in Java. There are many other forms as listed in the JLS:
MethodInvocation:
MethodName ( [ArgumentList] )
TypeName . [TypeArguments] Identifier ( [ArgumentList] ) <--- this is the form in question
ExpressionName . [TypeArguments] Identifier ( [ArgumentList] )
Primary . [TypeArguments] Identifier ( [ArgumentList] )
super . [TypeArguments] Identifier ( [ArgumentList] )
TypeName . super . [TypeArguments] Identifier ( [ArgumentList] )
TypeArguments:
< TypeArgumentList >
TypeArgumentList:
TypeArgument {, TypeArgument}
Inside the <...>
, you are supposed to write the type arguments for each of the type parameters of the generic method explicitly.
Note that ClassName<Type>.methodName
is not one of the forms.
The main thing you missed in your attempt to simulate this behaviour in plain Java is that GenericInterface.doJob
should take a parameter, because the lambda you pass in has a parameter:
param -> processParam(param)
^^^^^
This parameter should be of type GenericClass<T>
, because that is what processParam
takes.
interface GenericInterface <T> {
void doJob(GenericClass<T> param);
}
This is analogous to the subscribe
method in FlowableOnSubscribe<T>
, which is what Flowable.create
takes as argument.
Note that subscribe
takes a different type from what Flowable.create
returns, but we are taking the same type as what GenericClass.genericMethod
returns. This still simulates the same behaviour of "writing the type parameter explicitly compiles but omitting it produces an error", so I will not bother to create a new type.
Now you get the same behaviour as the Flowable.create
method in RxJava:
GenericClass.genericMethod(param -> processParam(param)); // error
GenericClass.<Integer>genericMethod(param -> processParam(param)); // OK
This is because Java's type inference algorithm is not powerful enough to infer the type correctly in the first case.
There is still a difference between this and RxJava though - Flowable.create
takes a FlowableOnSubscribe<T>
, which has a ``
CodePudding user response:
You can expand your example to:
class Sample2 {
public static void main(String[] args) {
GenericClass.genericMethod(new GenericInterface<Integer>() {
@Override
public Integer doJob(? param) {
return param;
}
}));
}
private static void processParam(GenericClass<Integer> b) {
}
}
}
class GenericClass <T> {
public static <T> GenericClass<T> genericMethod(GenericInterface<T> genericinterface) {
return new GenericClass<T>();
}
}
interface GenericInterface <T> {
T doJob();
}
As you can see your lambda expression expects some input parameter "? param" which does not match the signature of GenericInterface#doJob. If you change your code to this:
lass Sample2 {
public static void main(String[] args) {
GenericClass.genericMethod(param -> processParam(param)); // <- this compiles now without any "<..>"
}
private static void processParam(GenericClass<Integer> b) {
}
}
}
class GenericClass <T> {
public static <T> GenericClass<T> genericMethod(GenericInterface<T> genericinterface) {
return new GenericClass<T>();
}
}
interface GenericInterface <T> {
T doJob(T param);
}
When you struggle with lamdas and Functional Interfaces try expanding them to their anonymous inner class counterparts. This can help your IDE to give your better feet back and makes mistakes like this more obvious.