泛型的目标之一就是能够编写尽可能广泛应用的代码。

为了实现这一点,我们需要各种途径来放松对我们的代码将要作用的类型所做的限制,同时不丢失静态类型检查的好处。即写出更加泛化的代码。

Java泛型看起来是向这个方向迈进了一步。但是还是有一定的限制:

(1)当你在编写或使用只是持有对象<T>的泛型时,这些代码可以适用于任何类型。这些代码就可以真正应用于任何地方,因此相当泛化。

(2)当要在泛型类型上执行操作时,就会产生问题,因为擦除要求指定可能会用到的泛型类型的边界,以安全的调用代码中的泛型对象上的具体方法。

这是对泛化的明显限制。因为必须限制你的泛型类型,使它们继承特定类或实现特定接口。

某些编程语言(Python、C++)提供一种解决方案称为潜在类型机制,它只要求泛型代码实现某个方法的子集,而不是特定类或接口。

尽管Java不支持潜在类型机制,但这并不意味着有界泛型代码不能在不同的类型层次结构之间应用,只不过需要付出一些额外的努力。

(0)以下面例子作为原型:

 interface Performs {
void speak();
void sit();
}
class Dog implements Performs {
@Override public void speak() { System.out.println("Dog Speaking..."); }
@Override public void sit() { System.out.println("Dog Sitting..."); }
public void run() { System.out.println("Dog Running..."); }
}
class Robot implements Performs {
@Override public void speak() { System.out.println("Robot Speaking..."); }
@Override public void sit() { System.out.println("Robot Sitting..."); }
public void calc() { System.out.println("Robot Calculating..."); }
}
class Actions {
public static void perform(Performs p) {
p.speak();
p.sit();
}
}
public class DogsAndRobots {
public static void main(String[] args) {
Actions.perform(new Dog());
Actions.perform(new Robot());
}
}

(1)反射

 import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; class Dog1 {
public void speak() { System.out.println("Dog1 Speaking..."); }
public void sit() { System.out.println("Dog1 Sitting..."); }
public void run() { System.out.println("Dog1 Running..."); }
}
class Robot1 {
public void speak() { System.out.println("Robot1 Speaking..."); }
public void sit() { System.out.println("Robot1 Sitting..."); }
public void calc() { System.out.println("Robot1 Calculating..."); }
}
class Actions1 {
public static void perform(Object obj) {
Class<?> objClass = obj.getClass();
try {
Method speak = objClass.getMethod("speak");
speak.invoke(obj);
Method sit = objClass.getMethod("sit");
sit.invoke(obj);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
public class DogsAndRobots1 {
public static void main(String[] args) {
Actions1.perform(new Dog1());
Actions1.perform(new Robot1());
}
}

(2)将一个方法应用于序列 (apply方法可以接受任何实现Iterable接口的事物,然而,这样的代码还是不够泛化。)

 import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List; class Dog2 {
public void speak() { System.out.println("Dog2 Speaking..."); }
public void sit() { System.out.println("Dog2 Sitting..."); }
public void run(int length) { System.out.println("Dog2 runs " + length + " miles."); }
} class SmartDog extends Dog2 {} class Actions2 {
// PECS
public static <T, S extends Iterable<? extends T>> void apply(S seq, Method f, Object... args) {
seq.forEach(p -> {
try {
f.invoke(p, args);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
});
}
} class FilledList<T> extends ArrayList<T> {
// PECS
public FilledList(Class<? extends T> type, int size) throws Exception {
for(int i = 0; i < size; i++) {
add(type.newInstance());
}
}
} public class DogsAndRobots2 {
public static void main(String[] args) throws Exception {
List<Dog2> dog2s = new ArrayList<>();
for (int i = 0; i < 3; i++) {
dog2s.add(new Dog2());
}
Actions2.apply(dog2s, Dog2.class.getMethod("speak"));
Actions2.apply(new FilledList<>(SmartDog.class, 2), SmartDog.class.getMethod("run", int.class), 1);
}
}

(3)用适配器仿真潜在类型机制

 import java.util.*;

 interface Addable<T> { void add(T t); }

 interface Generator<T> { T next(); }

 class SimpleQueue<T> implements Iterable<T> {
private LinkedList<T> storage = new LinkedList<T>();
public void add(T t) { storage.offer(t); }
public T get() { return storage.poll(); }
public Iterator<T> iterator() {
return storage.iterator();
}
} class Dog3 {
public void speak(){ System.out.println("Dog2 Speaking..."); }
public void sit() { System.out.println("Dog2 Sitting..."); }
public void run(int length) { System.out.println("Dog2 runs " + length + " miles."); }
} class BigDog extends Dog3 {}
class SmallDog extends Dog3 {} class Fill {
// Classtoken version:
public static <T> void fill(Addable<T> addable, Class<? extends T> classToken, int size) {
for (int i = 0; i < size; i++)
try {
addable.add(classToken.newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
} // Generator version:
public static <T> void fill(Addable<T> addable, Generator<T> generator, int size) {
for (int i = 0; i < size; i++)
addable.add(generator.next());
}
} // To adapt a base type, you must use composition.
// Make any Collection Addable using composition:
class AddableCollectionAdapter<T> implements Addable<T> {
private Collection<T> c; public AddableCollectionAdapter(Collection<T> c) {
this.c = c;
} public void add(T item) {
c.add(item);
}
} // A Helper to capture the type automatically:
class Adapter {
public static <T> Addable<T> collectionAdapter(Collection<T> c) {
return new AddableCollectionAdapter<>(c);
}
} // To adapt a specific type, you can use inheritance.
// Make a SimpleQueue Addable using inheritance:
class AddableSimpleQueue<T> extends SimpleQueue<T> implements Addable<T> {
public void add(T item) { super.add(item); }
} public class DogsAndRobots3 {
public static void main(String[] args) {
List<Dog3> dog3s = new ArrayList<>();
// Adapt a Collection:
Fill.fill(new AddableCollectionAdapter<>(dog3s), BigDog.class, 3);
// Helper method captures the type:
Fill.fill(Adapter.collectionAdapter(dog3s), SmallDog.class, 2);
for(Dog3 elem : dog3s) {
System.out.println(elem.getClass().getSimpleName());
}
System.out.println("----------------------");
// Use an adapted class:
AddableSimpleQueue<Dog3> dog3Queue = new AddableSimpleQueue<>();
Fill.fill(dog3Queue, BigDog.class, 4);
Fill.fill(dog3Queue, SmallDog.class, 1);
for(Dog3 elem : dog3Queue) {
System.out.println(elem.getClass().getSimpleName());
}
// BigDog
// BigDog
// BigDog
// SmallDog
// SmallDog
// ----------------------
// BigDog
// BigDog
// BigDog
// BigDog
// SmallDog
}
}

Java泛型(11):潜在类型机制的更多相关文章

  1. JAVA混型和潜在类型机制

    一.混型 ①.定义 二.利用JAVA如何实现混型 ①.代理   ②.装饰器模式  ③.动态代理模式   ④.装饰器模式与代理模式的区别 三.潜在类型机制 ①.定义 四.JAVA的潜在类型机制的补偿 ① ...

  2. Java泛型中的类型擦除机制简单理解

    Java的泛型是JDK1.5时引入的.下面只是简单的介绍,不做深入的分析. Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...

  3. Java泛型-内部原理: 类型擦除以及类型擦除带来的问题

    一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...

  4. java泛型总结(类型擦除、伪泛型、陷阱)

    JDK1.5开始实现了对泛型的支持,但是java对泛型支持的底层实现采用的是类型擦除的方式,这是一种伪泛型.这种实现方式虽然可用但有其缺陷. <Thinking in Java>的作者 B ...

  5. java 泛型没有协变类型, 所以要重用extends, 但使用List<? extends Fruit> 可以是ArrayList<Fruit>()、ArrayList<Apple>()、ArrayList<Orange>(), 因此不能add元素进去

    class Fruit{} class Apple extends Fruit{} class SubApple extends Apple{} class Orange extends Fruit{ ...

  6. JAVA泛型中的类型擦除及为什么不支持泛型数组

    一,数组的协变性(covariant array type)及集合的非协变性 设有Circle类和Square类继承自Shape类. 关于数组的协变性,看代码: public static doubl ...

  7. 使用Java泛型返回动态类型

    返回一个指定类型的集合,并且clazz必须继承IGeoLog对象或者是其本身 <T extends IGeoLog> List<T> getLogListSql(Class&l ...

  8. java 泛型思考

    java泛型并没有像C++那样原生支持,因此,为了保证迁移兼容性,编译器在编译时会擦除具体类型,因此不能通过泛型调用具体方法. 如果调用必须用extends关键字限定范围,也正是由于这个原因,java ...

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

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

随机推荐

  1. okhttp拦截器之CacheInterceptor解析

    在上一次[https://www.cnblogs.com/webor2006/p/9150658.html]了解了缓存的存与取的细节之后,接下来就可以分析一下OkHttp的缓存拦截器啦: OkHttp ...

  2. eclipse设置打开java文件目录

    1. 第一步: 2. 第二步: 3. 第三步: Location:C:/WINDOWS/explorer.exe Arguments:${container_loc}

  3. windows 10安装mongodb数据库

    简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当 ...

  4. 理解Event冒泡模型

    本文探索一下Event的冒泡过程和初学遇到的几个小bug DOM Event概述 Event接口是检测在DOM中的发生的所有事件,我们一直在用,而且从DOM的很早的版本就一直在用着.早期的网景(后来的 ...

  5. osm3ge

    https://www.acugis.com/opentileserver/ https://openmaptiles.org/docs/ https://www.maptiler.com/?_ga= ...

  6. QueryList之flatten方法

    正确用法: $data = $ql->get($url)->query()->getData();$data = $data->flatten()->all(); 注意: ...

  7. JSP大文件分片上传

    核心原理: 该项目核心就是文件分块上传.前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题. * 如何分片: * 如何合成一个文件: * 中断了从哪个分片开 ...

  8. 小米 oj 硬币比赛(思维+动态规划)

     硬币比赛 序号:#47难度:困难时间限制:1000ms内存限制:10M 描述 有 n 个不同价值的硬币排成一条线.有 A 与 B 两个玩家,指定由 A 开始轮流(A 先手,然后 B,然后再 A..) ...

  9. Codeforces 1238E. Keyboard Purchase

    传送门 注意到 $m$ 只有 $20$ ,考虑一下状压 $dp$ 设 $f[S]$ 表示当前确定的字符集合为 $S$ ,那么转移就考虑从最右边加入的下一个字符 $c$ 那么问题来了,代价如何计算 考虑 ...

  10. codeforces gym #101161G - Binary Strings(矩阵快速幂,前缀斐波那契)

    题目链接: http://codeforces.com/gym/101161/attachments 题意: $T$组数据 每组数据包含$L,R,K$ 计算$\sum_{k|n}^{}F(n)$ 定义 ...