应该何时使用具有单个有界类型参数的泛型类?

时间:2016-05-20 11:21:05

标签: java

我很高兴收到(潜在的,截至撰写本文时)潜行者的反馈。

让我们假设我想创建一个存储类XXX的单个对象及其所有派生类的类。
我应该采用LSP方法还是采用泛型有界类型参数方法? 在我看来,考虑到Lieskov替换原则(LSP)的含义,具有单个有界类型参数的泛型类是不必要的: 以下两个类都接受相同类型的对象(作为示例我使用下面的类Object及其派生类)。

class LSP { //this class uses Lieskov Substitution Principle (LSP)
    Object a;
    LSP(Object a) { this.a= a; }
    Object get() { return this.a; }
}
class Gen<V  extends Object> { //this is a generic class with bounded type parameter 
    V a;
    Gen(V a) { this.a= a; }
    V get() { return this.a; }
}
class Stackoverflow {
    public static void main(String[] arg){
        LSP l = new LSP(5);
        Gen<Integer> g = new Gen<Integer>(5);
    }
}

你能指出我可能的分歧吗?好处?缺陷?

3 个答案:

答案 0 :(得分:3)

假设你有一个这样的课程:

class SomeClass<T extends Number> {
  T instance;
  SomeClass(T instance) { this.instance = instance; }
  T get() { return instance; }
  int intValue() { return instance.intValue(); }
}

要调用instance.intValue()方法,T必须是具有intValue方法的类型。通过约束T扩展Number,我们保证这一点。

答案 1 :(得分:1)

用你的例子我可以做到:

LSP l = new LSP(5);
Integer i = (Integer) l.get();

需要演员阵容,但这不是一个安全演员。我也可以这样做:

LSP l = new LSP(new Object());
Integer i = (Integer) l.get(); // Crash

使用泛型,如果我不使用原始类型,我总是可以确保至少来自get整数:

Gen<Integer> g = new Gen<>(new Object()); // Does not compile
...
Gen<Integer> g2 = new Gen<>(5);
Integer i = g2.get(); // no need to cast

答案 2 :(得分:0)

如果您发现错误或不完整,请使用评论来帮助我改进对自己问题的摘要答案。再次感谢所有帮助我做到这一点的人。

假设Base类:

class Base {}

并将使用其派生类,例如:

class Der1 extends Base {}
class Der2 extends Base {}
Der1 d1 = new Der1();
Der2 d2 = new Der2();

...可以选择通过使用泛型或利用两者具有相同Der1父级的事实将Der2Base实例同时接收到其他类中=利用Lieskov替代原则,LSP):

class Gen<T extends Base> { //this class uses bounded generics
    T o;
    void set(T o){ this.o = o; }
    T get(){ return o; }
}
class LSP{              // this class uses LSP
    Base o;
    void set(Base o){ this.o = o; }
    Base get(){ return o; }
}

<强> 1。在为LSP类实例化它们时,GenBase之间几乎没有区别,例如:

Gen<Base> g = new Gen<Base>();
LSP l = new LSP();

...因为这两个类在接受Der1Der2时非常相似:

g.set(d1);
g.set(d2);
l.set(d1);
l.set(d2);

...并且这两个类都具有强制转换的特性,在运行时不安全,因为不明显哪个派生类型实际存储在LSPGen中:

d2 = (Der2) g.get(); //results in runtime error if `Der1` has been stored in `g`
d2 = (Der2) l.get();  //results in runtime error if `Der1` has been stored in `l`

<强> 2。泛型Gen在为派生类实例化时具有不同的特征,例如:

Gen<Der1> g1 = new Gen<>();
Gen<Der2> g2 = new Gen<>();

现在,编译器能够确保类型安全,同时将数据放入Gen<>,以及从Gen<>获取数据时,代价是为每个派生类型单独的Gen<>实例:

g1.set(d1); //can only put d1 to g1
g2.set(d2); //can only put d2 to g2
d1 = g1.get(); // no casting needed, only d1 can be in g1
d2 = g2.get(); // no casting needed, only d2 can be in g2