2020-09-15-Java泛型中的自限定泛型

Java的自限定泛型

一、古怪的循环

古怪的循环泛型(CRG):类相当古怪地出现在出现在它的基类中。

我创建一个新类,它继承自一个泛型类型,这个泛型类型接受我的类的名字作为其参数。

class GenericType<T>{}
// 古怪的循环: 类相当古怪地出现在它自己地基类中
public class CuriouslyRecurringGeneric extends GenericType<CuriouslyRecurringGeneric>{

Java的泛型关乎参数和返回类型,因此它能够产生使用导出类作为其参数和返回类型的基类,它还能将导出类型作为其域类型。

CRG的本质:基类用导出类代替其参数。

这意味着泛型基类变成了一种其所有导出类的公共功能的模版,但是这些功能对于其所有参数和返回值,将使用导出类型。也就是说,在所产生的类中使用确切类型而不是基类型。

public class BasicHolder<T> {
    T element;
    void set(T arg){element = arg;}
    T get(){return element;}
    void f(){
        System.out.println(element.getClass().getSimpleName());
    }
}
// 
class Subtype extends BasicHolder<Subtype>{}
public class CRGWithBasicHolder {
    public static void main(String[] args) {
        Subtype st1 = new Subtype(), st2 = new Subtype();
        st1.set(st2);
        Subtype st3 = st1.get();
        st1.f();
    }
}

上面代码中BasicHolder可以使用任何类型作为其泛型参数,就像下面:

class Other{}
class BasicOther extends BasicHolder<Other>{}

为了强制要求将正在定义的类作为参数传递给基类,需要使用自限定

二、自限定

自限定:class SelfBounded<T extendsSelfBounded>, 可以保证类型参数必须与正在定义的类相同。

自限定只能强制作用于继承关系。

class SelfBounded<T extends SelfBounded<T>>{
    T element;
    SelfBounded<T> set(T arg){
        element = arg;
        return this;
    }
    T get(){return element;}
}
class A extends SelfBounded<A>{}
class A extends SelfBounded<A>{}
class B extends SelfBounded<A>{}
class C extends SelfBounded<C>{}
class D {}

/** 下面这种会报编译错误 */
//class E extends SelfBounded<D>{}

class F extends SelfBounded{}

三、自限定类型的价值——产生协变参数类型

3.1 协变返回类型

class Base{}
class Derived extends Base{}
interface OrdinaryGetter{
    Base get();
}
interface DerivedGetter extends OrdinaryGetter{
    // 覆盖的方法允许返回类型不同
    @Override
    Derived get();
}
public class CovariantReturnTypes {
    void test(DerivedGetter d){
        Derived d2 = d.get();
    }
}

3.2 自限定泛型事实上将产生确切的导出类型作为其返回值

协变参数类型——方法参数类型会随着自类而变化,自限定类型还可以产生于子类类型相同的返回类型。

在非泛型代码中,参数类型不能随子类型发生变化。

class OrdinarySetter{
    void set(Base base){
        System.out.println("OrdinarySetter.set(base)");
    }
}
class DerivedSetter extends OrdinarySetter{
    void set(Derived derived){
        System.out.println("DerivedSetter.set(derived)");
    }
}
public class OrdinaryArguments {
    public static void main(String[] args) {
        Base base = new Base();
        Derived derived = new Derived();
        DerivedSetter ds = new DerivedSetter();
        ds.set(derived);
        ds.set(base);
    }
}
/*
DerivedSetter.set(derived)
OrdinarySetter.set(base)
*/

上面set(derived)set(base)都是合法的,因此DerivedSetter.set()没有覆盖OrdinarySetter.set(),而是重载了这个方法。

但是,在使用自限定类型时,在导出类中只有一个方法,并且这个方法接受导出类型而不是基类型作为参数。

interface SelfBoundingSetter<T extends SelfBoundingSetter<T>>{
    void set(T arg);
}
interface Setter extends SelfBoundingSetter<Setter>{}
public class SelfBoundingAndCovariantArguments {
    void testA(Setter s1, Setter s2, SelfBoundingSetter sbs){
        s1.set(s2);
        /** 
         * 下面这个调用会报编译错误
         * 编译期不能识别将基类当作参数传递给set()的尝试,因为没有任何方法具有这样的签名
         * 实际上,这个参数已经被覆盖
         * */
//        s1.set(sbs);
    }
}

如果不使用自限定类型,普通的继承机制就会介入,而你能够重载,就像在非泛型的情况下一样:

class GenericSetter<T> {
    void set(T arg){
        System.out.println("GenericSetter.set(base)");
    }
}
class DerivedGS extends GenericSetter<Base>{
    void set(Derived derived){
        System.out.println("DerivedGS.set()");
    }
}
public class PlainGenericInheritance{
    public static void main(String[] args) {
        Base base = new Base();
        Derived derived = new Derived();
        DerivedGS derivedGS = new DerivedGS();
        derivedGS.set(derived);
        derivedGS.set(base);
    }
}
阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲲鹏飞九万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值