在这个Java类层次结构中,如何使用泛型以便不需要类型转换?

时间:2015-03-20 21:28:50

标签: java class-hierarchy generics

假设我有两个名为RealNumberIntNumber的类,以及一个名为plus的方法:

public class RealNumber<S extends RealNumber> {
    public S plus(S realNumber) {
        ...
        }
    }

public class IntNumber
      extends RealNumber<IntNumber> { }

使用plus方法时,我收到一些编译器警告和错误:

RealNumber x = new RealNumber();
IntNumber y = new IntNumber();

RealNumber sum1 = x.plus(x); // Warning: Unchecked call to 'plus(S)' as a member of raw type 'RealNumber'.
RealNumber sum2 = x.plus(y); // Warning: Unchecked call to 'plus(S)' as a member of raw type 'RealNumber'.
RealNumber sum3 = y.plus(x); // Error: plus (IntNumber) in RealNumber cannot be applied to (RealNumber).
RealNumber sum4 = y.plus(y); // This works fine.
IntNumber  sum5 = y.plus(x); // Error: plus (IntNumber) in RealNumber cannot be applied to (RealNumber).
IntNumber  sum6 = y.plus(y); // This works fine.

要解决此问题,我修改了plus方法,如下所示:

public <T extends RealNumber> S plus(T realNumber) {
    ...
    }

现在一切正常。好! 但是,我现在想要添加一个名为PositiveIntNumber的第三个类,它扩展了IntNumber

    public class PositiveIntNumber extends IntNumber { }

当然,这不起作用:

RealNumber x = new RealNumber();
IntNumber y = new IntNumber();
PositiveIntNumber z = new PositiveIntNumber();

RealNumber sum1 = x.plus(x); // Fine.
RealNumber sum2 = x.plus(y); // Fine.
RealNumber sum3 = y.plus(x); // Fine.
RealNumber sum4 = y.plus(y); // Fine.
IntNumber sum5 = y.plus(x);  // Fine.
IntNumber sum6 = y.plus(y);  // Fine.
RealNumber sum7 = x.plus(z); // Fine.
IntNumber sum8 = y.plus(z);  // Fine.
PositiveIntNumber sum9 = z.plus(x);  // Error: incompatible types: no instance(s) of type variable(s) T exist so that IntNumber conforms to PositiveIntNumber
PositiveIntNumber sum10 = z.plus(y); // Error: incompatible types: no instance(s) of type variable(s) T exist so that IntNumber conforms to PositiveIntNumber
PositiveIntNumber sum11 = z.plus(z); // Error: incompatible types: no instance(s) of type variable(s) T exist so that IntNumber conforms to PositiveIntNumber

为了再次修复此问题,我修改了类定义,如下所示:

public class IntNumber<S extends IntNumber>
      extends RealNumber<S> { }

public class PositiveIntNumber
      extends IntNumber<PositiveIntNumber>
    { }

这解决了PositiveIntNumber的问题,但中断了IntNumber

RealNumber x = new RealNumber();
IntNumber y = new IntNumber();
PositiveIntNumber z = new PositiveIntNumber();

RealNumber sum1 = x.plus(x); // Fine.
RealNumber sum2 = x.plus(y); // Fine.
RealNumber sum3 = y.plus(x); // Fine.
RealNumber sum4 = y.plus(y); // Fine.
IntNumber sum5 = y.plus(x);  // Error: incompatible types: RealNumber cannot be converted to IntNumber.
IntNumber sum6 = y.plus(y);  // Error: incompatible types: RealNumber cannot be converted to IntNumber.
RealNumber sum7 = x.plus(z); // Fine.
IntNumber sum8 = y.plus(z);  // Error: incompatible types: RealNumber cannot be converted to IntNumber.
PositiveIntNumber sum9 = z.plus(x);  // Fine.
PositiveIntNumber sum10 = z.plus(y); // Fine.
PositiveIntNumber sum11 = z.plus(z); // Fine.

演员修复了它,但在我看来,它应该是不必要的:

IntNumber sum5 = (IntNumber)y.plus(x);

所以,我有两个问题:

1)由于yIntNumber,而返回类型S扩展为IntNumber,为什么y.plus(...)会返回RealNumber }?

2)如何解决这个问题?

修改:写RealNumber<RealNumber>应该是不必要的,因为RealNumber<IntNumber>RealNumber<PositiveIntNumber>完全没有意义。因此,像这样的泛型的全部使用可能是完全错误的。我认为这回答了问题1:y.plus(...)返回RealNumber因为y是原始的,所以Java类型系统不再关心S扩展IntNumber。但是,我想避免在RealNumber的所有子类中重复plus方法,并且我应该能够以某种方式使用泛型来避免这种情况。问题2仍然存在。我该怎么办?

注意:我的实际类不是Reals和Integers,而是一些其他复杂的业务类。将它们视为A,B,C类,不要质疑模型。它们是“可添加的”,是的,但是这里X.plus(Y)应该为每个X和Y返回X类型。

2 个答案:

答案 0 :(得分:2)

我假设你要创建的是一个表达&#34; Addables&#34;返回,给出两个&#34;操作数&#34;,更一般的两个,使用继承来模拟&#34;特殊情况&#34;的数学关系。

在进一步讨论之前,你应该考虑使用Square is-a Rectangle示例的this link来说明为什么继承的使用通常是一个坏主意,但是对于像你这样的不可变对象的情况。 #39;重新提议你可能没事。

您希望Int.plus(Real)和Real.plus(Int)都返回Real,而Int.plus(Int)返回Int。相同的模式应该适用于Nat.plus(Int)等。

只要所有类型静态已知,那应该是类似的小菜一碟。

class Real {
    Real plus( Real r ) { ... }
    Int floor() { ... }
}
class Int extends Real {
    Int plus( Int i ) {
        return plus( i.asReal() ).floor();
    }
    Nat abs() { ... }
    Real asReal() {
        return this;
    }
}
class Nat extends Int {
    Nat plus( Nat n ) {
        return plus( n.asInt() ).abs();
    }
    Int asInt() {
        return this;
    }
}

重载plus允许编译器确定&#34;最具体的&#34;加上可以静态验证的操作。编译器不能知道添加两个恰好是整数的实数会产生另一个真实的整数。就此而言,一些非整数加起来为整数;我确定你不打算让类型系统以某种方式表示这个事实。

根据这种安排,除了sum5sum9sum10类型检查之外的所有内容都是(在我看来)完全一样。

顺便提一下,关于&#34;奇怪的重复出现的类型约束&#34;你试图证明的模式,你做得有些错误。你需要提供一个类型参数无处不在发生泛型类型,甚至在类型绑定本身内:

class G< T extends G< T > > {}

您和此处的所有其他评论者和响应者一直在离开第二个T

答案 1 :(得分:1)

以上所有评论都是正确的。如果你定义了一个泛型类,你也应该定义它。如果你使用它。

public class GenericNumber {

    public static void main(String[] args) {
        // ------
        // ------ The important change
        // ------
        RealNumber<RealNumber> x = new RealNumber<>();            
        IntNumber<IntNumber> y = new IntNumber<>();
        PositiveIntNumber z = new PositiveIntNumber();
        // Everything works fine
        RealNumber sum1 = x.plus(x); 
        RealNumber sum2 = x.plus(y); 
        RealNumber sum3 = y.plus(x); 
        RealNumber sum4 = y.plus(y); 
        IntNumber sum5 = y.plus(x); 
        IntNumber sum6 = y.plus(y); 
        PositiveIntNumber sum9 = z.plus(x);  
        PositiveIntNumber sum10 = z.plus(y); 
        PositiveIntNumber sum11 = z.plus(z); 
    }

    public static class RealNumber<S extends RealNumber> {

        public <T extends RealNumber> S plus(T realNumber) {
            return null;
        }
    }

    public static class IntNumber<S extends IntNumber> extends RealNumber<S> {
    }

    public static class PositiveIntNumber extends IntNumber<PositiveIntNumber> {
    }

}