Java合作泛型类:我们可以避免未经检查的强制转换吗?

时间:2016-01-26 11:48:26

标签: java generics compiler-errors

编辑:对不起;渴望给你一个最小的例子,我没有在我的第一个版本的问题中提供有关要求的足够信息。

我有两个抽象的泛型类。他们合作,因此相互依赖。偶尔需要将this传递给另一个。我正在努力找到一种安全的方式来做到这一点。

public abstract class AbstractA<T extends AbstractB<? extends AbstractA<T>>> {

    protected void foo() {
        T aB = createB();
        aB.setA(this);
    }

    /** factory method */
    abstract public T createB();

}

public abstract class AbstractB<T extends AbstractA<? extends AbstractB<T>>> {

    private T theA;

    @SuppressWarnings("unchecked")
    public void setA(AbstractA<? extends AbstractB<?>> theA) { // dreamed of parameter list (T theA)
        // Unchecked cast from AbstractA<capture#1-of ? extends AbstractB<?>> to T
        this.theA = (T) theA;
    }

    protected T getA() {
        return theA;
    }

}

我的问题是我是否能找到更清洁的方式,以避免AbstractB.setA()中未经检查的演员表。我原本希望声明它setA(T theA),但是对它的调用将无法编译:方法setA(捕获#1-of?extends AbstractA&lt; T&gt;)在AbstractB&lt; capture#1-of?中?扩展AbstractA&lt; T&gt;&gt;不适用于参数(AbstractA&lt; T&gt;)。我仍在努力了解编译器是否应该知道是否允许它。

我在想我的问题可能与Java generics compilation error - The method method(Class<capture#1-of ? extends Interface>) in the type <type> is not applicable for the arguments中讨论的问题有关。我的未经检验的演员从那里受到启发。我喜欢Tom Hawtin的回复 - 攻击,但我还没有办法将它应用到我的情况中。

我的用户将声明具体的子类并实例化一个ConcreteA和任意数量的ConcreteB s:

public class ConcreteA extends AbstractA<ConcreteB> {

    @Override
    public ConcreteB createB() {
        return new ConcreteB();
    }

    public void concreteAMethod() {
        // ...
    }

}

public class ConcreteB extends AbstractB<ConcreteA> {

    public void bar() {
        ConcreteA a = getA();
        a.concreteAMethod();
    }

}

class AbstractA<T extends AbstractB<? extends AbstractA<T>>>看起来有点复杂;我认为我需要它来具体的子类来了解彼此的确切类型,但显然它并没有给我这个。)

5 个答案:

答案 0 :(得分:0)

如果我理解正确,那么应该创建你想要的绑定。

class Demo {

    public static void main(String[] args) {
        ConcreteA a = new ConcreteA();
        ConcreteB b = new ConcreteB();
        a.foo(b);
        b = (ConcreteB) a.getB();
    }
}

abstract class AbstractA<T extends AbstractB<?>>{

    private AbstractB<?> b;

    public AbstractB<?> getB(){
        return b;
    }

    void foo(AbstractB<?> aB) {
        b = aB;
        aB.bar(this);
    }
}

abstract class AbstractB<T extends AbstractA<?>> {

    private AbstractA<?> a;

    public AbstractA<?> getA(){
        return a;
    }

    public void bar(AbstractA<?> theA) {
        a = theA;
        theA.foo(this);
    }
}

class ConcreteA extends AbstractA<ConcreteB>{

}

class ConcreteB extends AbstractB<ConcreteA>{

}

我认为这就是你自己的结果。我无法将转换移到ConcreteB,getB()根本无法确定它所持有的类型。我现在明白为什么你的声明中有多个通用语句。 :)

如果你准备好了,继续搜索,如果找到答案就发布你自己的答案,我很乐意看到它。

我希望解决你的一半问题。 ;)

答案 1 :(得分:0)

我想我现在知道为什么我不能在public void setA(T theA)中声明AbstractB,然后在aB.setA(this)中将其称为foo()。假设我们有:

class IntermediateConcreteA extends AbstractA<ConcreteB> {

    @Override
    public ConcreteB createB() {
        return new ConcreteB();
    }

}

class SubConcreteA1 extends IntermediateConcreteA {}

class SubConcreteA2 extends IntermediateConcreteA {}

class ConcreteB extends AbstractB<SubConcreteA2> {}

现在,如果我有一个SubConcreteA1并调用其foo(),那么createB()将返回一个可以作为AbstractB<SubConcreteA2>传递但不能作为{{1}传递的对象}}。因此,AbstractB<SubConcreteA1>不应接受setA()作为参数。毕竟编译器错误消息是合乎逻辑的。

答案 2 :(得分:0)

每个抽象类都将使用两个类型参数进行参数化,一个用于实际的具体类A,另一个用于实际的具体类B:

public abstract class AbstractA<A extends AbstractA<A,B>, B extends AbstractB<A,B>> {

    protected void foo() {
        B aB = createB();
        aB.setA(getThis());
    }

    abstract public A getThis();
    abstract public B createB();

}

public abstract class AbstractB<A extends AbstractA<A,B>, B extends AbstractB<A,B>> {

    private A theA;

    public void setA(A theA) {
        this.theA = theA;
    }

    protected A getA() {
        return theA;
    }

}

public class ConcreteA extends AbstractA<ConcreteA, ConcreteB> {

    @Override
    public ConcreteA getThis() {
        return this;
    }

    @Override
    public ConcreteB createB() {
        return new ConcreteB();
    }

    public void concreteAMethod() {
        // ...
    }

}

public class ConcreteB extends AbstractB<ConcreteA, ConcreteB> {

    public void bar() {
        ConcreteA a = getA();
        a.concreteAMethod();
    }

}

答案 3 :(得分:0)

工厂可以解决它:

public abstract class AbstractA {

    public void abstractAMethod() {
        // ...
    }

}

public abstract class AbstractB<A> {

    private A theA;

    public void setA(A theA) {
        this.theA = theA;
    }

    protected A getA() {
        return theA;
    }

}

public abstract class AbstractFactory<A extends AbstractA, B extends AbstractB<A>> {

    private A theA = createA();

    public A getA() {
        return theA ;
    }

    public B getNextB() {
        B newB = createB();
        newB.setA(theA);
        return newB;
    }

    protected abstract A createA();
    protected abstract B createB();
}

现在用户可以去:

public class ConcreteA extends AbstractA {

    public void concreteAMethod() {
        // ...
    }

}

public class ConcreteB extends AbstractB<ConcreteA> {

    public void bar() {
        ConcreteA a = getA();
        a.abstractAMethod();
        a.concreteAMethod();
    }

}

public class ConcreteFactory extends AbstractFactory<ConcreteA, ConcreteB> {

    @Override
    protected ConcreteA createA() {
        return new ConcreteA();
    }

    @Override
    protected ConcreteB createB() {
        return new ConcreteB();
    }

}

我不认为这是抽象工厂模式的典型应用,但是......

@Chris Wohlert,我确实放弃了我的生产代码,因为我认为工厂过度杀伤,但我无法理解这个理论问题。

答案 4 :(得分:0)

我已经意识到我的问题实际上是将两个概念填充到不属于一起的AbstractA / ConcreteA层次结构中。虽然对很多人来说可能没什么兴趣,但我发布这个见解有两个原因:(1)我觉得我欠Chris Wohlert的答案我发现自己(2)更重要的是,我喜欢激励其他任何面临类似狡猾的人泛型问题,从更高层次审查您的设计,而不仅仅是解决泛型和/或类演员问题。它当然帮助了我。演员/仿制品问题表明一些更基本的东西并不完全正确。

public abstract class AbstractA {

    public void foo() {
        AbstractB aB = createB();
        aB.setA(this);
    }

    /** factory method */
    abstract public AbstractB createB();

}

public abstract class AbstractB {

    private AbstractA theA;

    public void setA(AbstractA theA) {
        this.theA = theA;
    }

    // methods that use theA

}

没有泛型,也没有课堂演员。取出不属于A类层次结构的东西到ConcreteC(没有AbstractC):

public class Client {

    public void putTheActTogether() {
        ConcreteC theC = new ConcreteC();

        // the concrete A
        AbstractA theA = new AbstractA() {
            @Override
            public AbstractB createB() {
                return new ConcreteB(theC);
            }
        };
        // call methods in theA
    }

}

public class ConcreteB extends AbstractB {

    private final ConcreteC c;

    public ConcreteB(ConcreteC c) {
        super();
        this.c = c;
    }

    public void bar() {
        c.concreteCMethod();
    }

}

public class ConcreteC {

    public void concreteCMethod() { // was concreteAMethod(); moved and renamed
        // ...
    }

}

客户需要比以前更多的线路。在我的真实世界代码中,我需要在AbstractA和ConcreteC中复制一个最终字段,但这样做是有意义的。总而言之,我认为对于纯粹而简单的设计而言,它的价格很低。