Quiero tener una clase Java resumen así:
abstract class AbstractFoo<F extends AbstractFoo<F, L>, L extends FooListener<F, L>> {
private final Class<L> listenerClass;
protected AbstractFoo(Class<L> listenerClass) {
this.listenerClass = listenerClass;
}
interface FooListener<F extends AbstractFoo<F, L>, L extends FooListener<F, L>> {
void callback(F foo);
}
// Bar might implement FooListener, but I don't control it,
// so I have no guarantee
public void externalMethod(Bar bar) {
if (listenerClass.isInstance(bar)) {
L listener = listenerClass.cast(bar);
listener.callback(this); // does not compile
}
}
}
listener.callback(this);
no se compila porque no hay garantía de que this
es del mismo tipo que F
. ¿Es posible garantizar de alguna manera que F
es un subtipo de él this
?
Lo que estamos tratando de hacer es emular el tipo AUTO en Java utilizando genéricos. Vea este enlace o un sitio diferente para algunos medios para hacerlo. Sin embargo, no hay manera de hacer cumplir que F (o del tipo de auto) es en realidad el mismo tipo, por ejemplo (véase el parámetro tipo de ConcreteFoo2):
static class Bar implements FooListener<ConcreteFoo, Bar> {
@Override
public void callback(final ConcreteFoo foo) {
// TODO Auto-generated method stub
}
}
static class ConcreteFoo2 extends AbstractFoo<ConcreteFoo, Bar> {
protected ConcreteFoo2(final Class<Bar> listenerClass) {
super(listenerClass);
}
}
static class ConcreteFoo extends AbstractFoo<ConcreteFoo, Bar> {
protected ConcreteFoo(final Class<Bar> listenerClass) {
super(listenerClass);
}
}
En lugar de ir más allá de esta manera, me gustaría primero pensar en las opciones de diseño que haya que condujo hasta aquí:
¿Los oyentes realmente necesitan saber la clase concreta?
¿El AbstractFoo realmente necesita saber la aplicación concreta de la clase oyente?
Tal vez un menor número de parámetros de tipo son en realidad la solución, apoyándose en las interfaces solo.
EDIT: Una posible solución, si no quiere fundido (F) this
sería proporcionar un método abstracto protected abstract F getSelf();
que las implementaciones concretas implementan mediante la devolución this
.
Ver el código simplificado por ejemplo:
static final class Bar implements FooListener<ConcreteFoo> {
@Override
public void callback(final ConcreteFoo foo) {
// TODO Auto-generated method stub
}
}
static final class ConcreteFoo extends AbstractFoo<ConcreteFoo> {
protected ConcreteFoo(final Class<? extends FooListener<ConcreteFoo>> listenerClass) {
super(listenerClass);
}
@Override
protected ConcreteFoo getSelf() {
return this;
}
}
static abstract interface FooListener<FOO extends AbstractFoo<FOO>> {
void callback(FOO abstractFoo);
}
static abstract class AbstractFoo<SELF extends AbstractFoo<SELF>> {
private final Class<? extends FooListener<SELF>> listenerClass;
protected AbstractFoo(final Class<? extends FooListener<SELF>> listenerClass) {
this.listenerClass = listenerClass;
}
protected abstract SELF getSelf();
// Bar might implement FooListener, but I don't control it,
// so I have no guarantee
public void externalMethod(final Bar bar) {
if (listenerClass.isInstance(bar)) {
final FooListener<SELF> listener = listenerClass.cast(bar);
listener.callback(getSelf()); // compiles
}
}
}