自限定

自限定将强制泛型当做自己的边界参数来使用。自限定所做的,就是要求在继承关系中,像下面这样使用这个类:

class A extends SelfBounded<A> {}

它的意义是可以保证类型参数必须与正在被定义的类相同自限定只能强制作用于继承关系。如果使用自限定,就应该了解这个类所用的类型参数将与使用这个参数的类具有相同的基本类型。

下面是一个自限定的例子【1】:

 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 B extends SelfBounded<A> {} // It's OK. class C extends SelfBounded<C> {
C setAndGet(C arg) { set(arg); return get(); }
} class D {}
// class E extends SelfBounded<D> {} // [Compile error]: Type parameter D is not within its bound public class SelfBounding {
public static void main(String[] args) {
A a = new A();
a.set(new A());
a = a.set(new A()).get();
a = a.get();
C c = new C();
c = c.setAndGet(new C());
}
}

我们发现class E是不能编译的。如果移除自限定这个限制(class SelfBounded<T>),这样E就可以编译了。但是就不能限制E这样的非自限定类型继承SelfBounded类了。

参数协变

先看一个协变返回类型的例子【2】:

 class Base {}
class Derived extends Base {} interface OrdinaryGetter {
Base get();
} interface DerivedGetter extends OrdinaryGetter {
// DerivedGetter.get()覆盖了OrdinaryGetter.get()
@Override Derived get();
} public class CovariantReturnTypes {
void test(DerivedGetter d) {
Derived result1 = d.get(); // 调用的DerivedGetter.get()
Base result2 = d.get(); // 也调用的DerivedGetter.get()
}
}

而自限定泛型将产生确切的导出类型作为其返回值。请看例【3】:

 interface GenericGetter<T extends GenericGetter<T>> {
T get();
} interface Getter extends GenericGetter<Getter> {} public class GenericsAndReturnTypes {
void test(Getter g) {
Getter result1 = g.get();
GenericGetter result2 = g.get(); // Also the base type
}
}

例【2】可以证明,返回值并不是区分两个不同方法的途径。而下面的例【4】则说明参数类型可以区分两个不同的方法。所以例【2】是覆盖(override)而例【4】是重载(overload)。

 class OrdinarySetter {
void set(Base base) {
System.out.println("OrdinarySetter.set(Base)");
}
} class DerivedSetter extends OrdinarySetter {
// @Override // [Compile Error]: Can't override. It's overload not override!
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); // 调用DerivedSetter的set
ds.set(base); // 调用OrdinarySetter的set
}
}

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

 interface SelfBoundSetter<T extends SelfBoundSetter<T>> {
void set(T arg);
} interface Setter extends SelfBoundSetter<Setter> {} public class SelfBoundingAndCovariantArguments {
void testA(Setter s1, Setter s2, SelfBoundSetter sb1, SelfBoundSetter sb2) {
s1.set(s2);
// 编译器不能识别将基类型当做参数传递给set的尝试,因为没有任何方法具有这样的签名。事实上,这个参数已经被覆盖。
// s1.set(sb1); // [Compile Error]: set(Setter) in SelfBoundSetter<Setter> cannot be applied to (SelfBoundSetter)
sb1.set(s1);
sb1.set(sb2);
}
}

如果上例不使用自限定类型,普通继承机制就会介入,而你将能够重载,就像在非泛型的情况下一样(自限定类型可以避免这种情况发生):

 class GenericSetter<T> { // Not self-bounded
void set(T arg){
System.out.println("GenericSetter.set(Base)");
}
} class DerivedGS extends GenericSetter<Base> {
// @Override // [Compile Error]: Can't override. It's overload not override.
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); // Compiles: overloaded, not overridden!
}
}

Java泛型(8):自限定&参数协变的更多相关文章

  1. Java泛型之自限定类型

    在<Java编程思想>中关于泛型的讲解中,提到了自限定类型: class SelfBounded<T extends SelfBounded<T>> 作者说道: 这 ...

  2. Java泛型通配符以及限定

    摘抄笔记 A:泛型的限定 /* * 将的酒店员工,厨师,服务员,经理,分别存储到3个集合中 * 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 */ import java.uti ...

  3. Java泛型 类型变量的限定

    有时候,类和方法须要对类型变量加以约束.比方你有一个方法,你仅仅希望它接收某个特定类型及其子类型作为參数. 以下就举一个方法限定接收參数的类型的样例来说明怎样限定类型变量. 首先有几个简单的辅助类: ...

  4. Java泛型中的协变和逆变

    Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛 ...

  5. java 泛型基础问题汇总

    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引 ...

  6. Java泛型主题讨论

    说明:在学习泛型这一知识点中,主要参考自<疯狂Java讲义>第7章P307-P330的泛型内容,因为是跳着阅读,所以前面的一些名词不是特别清楚,这里也做出适当备注,供自己识记与理解. 1. ...

  7. Java泛型总结---基本用法,类型限定,通配符,类型擦除

    一.基本概念和用法 在Java语言处于还没有出现泛型的版本时,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化.例如在哈希表的存取中,JDK1.5之前使用HashMap的 ...

  8. JAVA泛型——协变

    在上篇<JAVA泛型——基本使用>这篇文章中遗留以下问题,即将子类型Table或者也能添加到父类型Auction的泛型中,要实现这种功能必须借助于协变. 实验准备 现在在<JAVA泛 ...

  9. Java泛型的协变

    在上篇<Java泛型的基本使用>这篇文章中遗留以下问题,即将子类型也能添加到父类型的泛型中,要实现这种功能必须借助于协变. 实验准备 现在在上篇文章展示的Decorator类型的基础上,增 ...

随机推荐

  1. 使用php实现javascript的escape和unescape函数

    javascript有编码函数escape()和对应的解码函数unescape(),而php中只有个urlencode和urldecode,这个编码和解码函数对encodeURI和encodeURIC ...

  2. 按照教程自动安装RFNoC时.在使用pip安装pybombs时出现报错,解决办法

    $ sudo apt-get install git $ sudo apt-get install python-setuptools python-dev python-pip build-esse ...

  3. 如何在SpringBoot的 过滤器之中注入Bean对象

    我建立一个全局拦截器,此拦截器主要用于拦截APP用户登录和请求API时候,必须加密,我把它命名为SecurityFilter,它继承了Filter,web应用启动的顺序是:listener->f ...

  4. mysql基础_数据类型

    1.数字 (1)tinyint(小整数值) 范围:有符号(-128,127) 无符号(0.255) (2)int(大整数值) 范围:有符号  (-2 147 483 648,2 147 483 647 ...

  5. use redir to make port redirecting

    Step 1: install redir apt-get update apt-get install redir -y Step2 : add port mapping redir --lport ...

  6. 记一次 解决 vue 兼容ie11 的问题

    vue2.0 最近项目需要做到兼容ie11,找问题找半天,于是各种百度, 发现引入 babel-polyfill 还是有问题  空白页面 参考它的配置 ( 最后一句代码为引入的资源路径 )  我跟他差 ...

  7. maven热部署插件-jetty

    作者:小勇Oo 关于maven-jetty-plugin的说明: pom文件中: <build> <finalName>freemarker</finalName> ...

  8. react-router5.x 的配置及其页面跳转方法和js跳转方法

    https://blog.csdn.net/sinat_37255207/article/details/90745207 上次用react-router 的时候  还是3.x 很久不用 已经到rea ...

  9. WebApiHelper

    /// <summary> /// 根据token过滤 /// </summary> /// <param name="list"></p ...

  10. EXE中释放DLL中分配的内存

    在DLL中分配的内存,如果到其调用者中释放,可能会出现CRASH的情况,其原因在于: 在DLL中的Code Generation如果是采用了MT(静态加载LIBCRTD.LIB)在该库中维护了一个al ...