I'm trying to get access to child class methods and fields, when creating lambda, which is used in parent class. Code explains it more easily:
class Parent {
List<Processor<? extends Parent>> processors;
private void doSmth() {
for (Processor<? extends Parent> processor : processors) {
processor.doJob(this); //this lines gives compile error
}
}
public void registerListeners(Processor<? extends Parent> ... subscribers) {
this.subscriberList = Arrays.asList(subscribers);
}
}
Where Processor
is a FunctionalInterface
.
public interface Processor<T extends Parent> extends BiFunction<T, Message, Boolean> {
AtomicReference<Boolean> ok = new AtomicReference<>(false);
default void doJob(T client, Message message) {
if (apply(client, message))
ok.set(true);
}
default boolean isDone() {
return ok.get();
}
}
The example of wanted usage of these classes:
Child childInstance= new Child(); //where Child class extends Parent
childInstance.registerListeners((child, message) -> child.callSomeChildMethod());
childInstance.doSmth(message);
It would be really cool to create lambda without redundant specifying of parameter type like in this line:
childInstance.registerListeners((Processor<Child>) (child, message) -> child.callSomeChildMethod());
(because it always should be type for which I register the listeners)
The problem is that code doesn't compile with errorincompatible types: Parent cannot be converted to capture#1 of ? extends Parent
Which is quite logical (I understand the reason). Is there some way in java I can get this code working?
Thanks in advance!
Your idea of having List<Processor<? extends Parent>> processors;
in Parent
class is not suggestible. As you see, since you have not mentioned the type of processes the list has; wherever you call processor.doJob(anyObjectHere)
an error gets thrown, one way or the other(unless you do explicit cast)
Try doing something like this;
Declare a Client
instead of your Parent
which holds the type of processors in List<Processor<? extends Parent>> processors
;
abstract class Client<T extends Client<T>> {
List<Processor<T>> processors;
public void doSmth(Message message) {
for (Processor<T> processor : processors) {
processor.doJob(getThis(), message);
}
}
abstract T getThis();
public void registerListeners(Processor<T> subscribers) {
this.processors = Arrays.asList(subscribers);
}
}
Change your Processor
definition to incorporating Client
rather that Parent
interface Processor<T extends Client<T>> extends BiFunction<T, Message, Boolean> {
AtomicReference<Boolean> ok = new AtomicReference<>(false);
default void doJob(T client, Message message) {
if (apply(client, message))
ok.set(true);
}
default boolean isDone() {
return ok.get();
}
}
Now you can create your Child
like this;
class Child extends Client<Child> {
boolean callSomeChildMethod() {
return true;
}
@Override
Child getThis() {
return this;
}
}
And call them the same way you did before;
Child childInstance= new Child(); //where Child class extends Parent
childInstance.registerListeners((child, message) -> child.callSomeChildMethod());
childInstance.doSmth(message);
This way you have neither compile errors nor warnings