《Java编程思想》参数协变
来源:互联网 发布:双色球分析软件 编辑:程序博客网 时间:2024/04/29 05:09
不得不吐槽一下,看《Java编程思想》收获的最多的当然是Java的知识,其次就是翻译的各种各样奇奇怪怪的单词。好了废话不多说,我们进入今天的正题:
解释一下参数协变或者协变参数类型其大概意思就是:方法的参数类型会随子类而改变,协变返回类型是Java SE5引入的。来看看下面的代码:
class Base {}class Derived extends Base{}interface OrdinaryGetter{ Base get();}interface DerivedGetter extends OrdinaryGetter{ Derived get();}class ConvarianReturnTypes{ @SuppressWarnings("unused") void test(DerivedGetter d) { Base d1 = d.get(); //下面的代码在早先的Java代码中是错误的 Derived d2 = d.get(); }}
可以看见,子类接口覆盖了基类接口的方法,从而可以返回子类接口指定的类型。再看看下面的代码:
interface GenericGetter<T extends GenericGetter<T>>{ T get();}interface Getter extends GenericGetter<Getter> {}class GenericsAndReturnTypes{ @SuppressWarnings("unused") void test(Getter g) { Getter result = g.get();//产生更加准确的导出类 GenericGetter gg = g.get();//所以可以转换为基类 }}
一定要注意,这个时候并不是一个方法能够返回不同的类型,而是只能返回一个确定的Getter这个子类,但是子类却可以被GenericGetter转型。
这段代码在Java SE5的环境下可以通过编译。再看看下面的代码:
class Base {}class Derived extends Base{}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); }}
注意这两行代码:
ds.set(derived);ds.set(base);
DerivedSetter类并没有使用泛型,所以也不存在协变,这是重载的结果。接下来看一看参数协变对于子类参数的影响,代码如下:
interface SelfBoundSetter<T extends SelfBoundSetter<T>>{ void set(T arg);}interface Setter extends SelfBoundSetter<Setter> {}class SelfBoundingAndCovariantArguments{ void testA(Setter s1,Setter s2,SelfBoundSetter sbs) { s1.set(s2); s1.set(sbs);//子类将不接受基类作为自己的参数 }}
可以看到,使用了自限定的语法之后,子类方法将只接受将自己作为参数,当然如果只是用泛型而不是自限定语法,这个目的同样能达到:
interface SelfBoundSetter<T>{ void set(T arg);}interface Setter extends SelfBoundSetter<Setter> {}class SelfBoundingAndCovariantArguments{ void testA(Setter s1,Setter s2,SelfBoundSetter sbs) { s1.set(s2); s1.set(sbs);//子类将不接受基类作为自己的参数 }}
两者没什么区别,甚至连报错原因都一样。我们可以这么理解——其实这里原来的 SelfBoundSetter的set方法是被子类给覆盖了,当然详细的解释还要看源码,在我把整本书看完之后,我会陆续填坑。当不使用自限定类型时,我们来看一看:
class Base {}class Derived{}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(Derived)"); }}public class PlainGenericInheritance{ public static void main(String[] args) { Base base = new Base(); Derived derived = new Derived(); DerivedGS dgs = new DerivedGS(); dgs.set(derived);//ok dgs.set(base);//ok }}
可以看到未使用自限定的时候,因为DerivedGS类的.set()方法可以接受两种参数,所DerivedGS的方法是被重载的。再来看看使用自限定方法的时候:
class Base {}class Derived{}class GenericSetter<T extends GenericSetter<T>>{ void set(T arg) { System.out.println("GenericSetter.set(Base)"); }}class DerivedGS extends GenericSetter<DerivedGS>//唯一的方法{ void set(Derived derived) { System.out.println("DerivedGS.set(Derived)"); }}public class PlainGenericInheritance{ public static void main(String[] args) { Base base = new Base(); Derived derived = new Derived(); DerivedGS dgs = new DerivedGS(); dgs.set(derived); dgs.set(base);//这个时候出现了参数不匹配 }}
这个时候只获得了一种方法,就是DerivedGS类自身的方法,很好理解,自限定的作用就是缩小参数范围,所以这个时候方法是被覆盖而不是重载。
总的来说,我认为自限定是一个不太实用的技巧,因为我对泛型的理解是增大方法和类的适用范围,但是自限定明显在缩小适用范围。
- 《Java编程思想》参数协变
- Java编程思想13.1-不可变的String
- 《Java编程思想》参数化的接口
- C# 4.0新特性——“协变”与“逆变”以及背后的编程思想
- C# 4.0新特性-"协变"与"逆变"以及背后的编程思想
- <Java编程思想> 多态与参数化类型
- java oop思想编程思想
- 好文章——C# 4.0新特性-"协变"与"逆变"以及背后的编程思想
- 关于编程思想和《java编程思想》
- Java编程思想读书笔记
- Java编程思想:引言
- java编程思想:
- Java编程思想学习
- Java编程思想 笔记
- java编程思想读书笔记
- java编程思想
- java 编程思想 读书笔记
- 学习java编程思想
- 集合
- Java中的异常和处理详解
- fjlfjlfjl
- hdu 5441 最小生成树
- find the nth digit
- 《Java编程思想》参数协变
- 线索二叉树
- Jenkins安装和初始化配置
- 大数据
- 简单的shell脚本学习
- 判断textarea是否超过行数限制
- HDU 6038 自环
- hdu5773-LIS&技巧-The All-purpose Zero
- 【HDU 6187 】Destroy Walls 【平面图+最大生成树】