不得不吐槽一下,看《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类自身的方法,很好理解,自限定的作用就是缩小参数范围,所以这个时候方法是被覆盖而不是重载。
总的来说,我认为自限定是一个不太实用的技巧,因为我对泛型的理解是增大方法和类的适用范围,但是自限定明显在缩小适用范围。