Java泛型(11):潜在类型机制
泛型的目标之一就是能够编写尽可能广泛应用的代码。
为了实现这一点,我们需要各种途径来放松对我们的代码将要作用的类型所做的限制,同时不丢失静态类型检查的好处。即写出更加泛化的代码。
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):潜在类型机制的更多相关文章
- JAVA混型和潜在类型机制
一.混型 ①.定义 二.利用JAVA如何实现混型 ①.代理 ②.装饰器模式 ③.动态代理模式 ④.装饰器模式与代理模式的区别 三.潜在类型机制 ①.定义 四.JAVA的潜在类型机制的补偿 ① ...
- Java泛型中的类型擦除机制简单理解
Java的泛型是JDK1.5时引入的.下面只是简单的介绍,不做深入的分析. Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
- Java泛型-内部原理: 类型擦除以及类型擦除带来的问题
一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...
- java泛型总结(类型擦除、伪泛型、陷阱)
JDK1.5开始实现了对泛型的支持,但是java对泛型支持的底层实现采用的是类型擦除的方式,这是一种伪泛型.这种实现方式虽然可用但有其缺陷. <Thinking in Java>的作者 B ...
- 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{ ...
- JAVA泛型中的类型擦除及为什么不支持泛型数组
一,数组的协变性(covariant array type)及集合的非协变性 设有Circle类和Square类继承自Shape类. 关于数组的协变性,看代码: public static doubl ...
- 使用Java泛型返回动态类型
返回一个指定类型的集合,并且clazz必须继承IGeoLog对象或者是其本身 <T extends IGeoLog> List<T> getLogListSql(Class&l ...
- java 泛型思考
java泛型并没有像C++那样原生支持,因此,为了保证迁移兼容性,编译器在编译时会擦除具体类型,因此不能通过泛型调用具体方法. 如果调用必须用extends关键字限定范围,也正是由于这个原因,java ...
- Java泛型总结---基本用法,类型限定,通配符,类型擦除
一.基本概念和用法 在Java语言处于还没有出现泛型的版本时,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化.例如在哈希表的存取中,JDK1.5之前使用HashMap的 ...
随机推荐
- LVS Nginx和HAproxy的区别,怎么选择最好
LVS Nginx和HAproxy有什么区别呢? LVS:Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统. Nginx:Nginx是一款轻量级的w ...
- C# 内存管理和指针 (13)
本章要点 运行库在栈和堆上分配空间 垃圾回收 使用析构函数 和 SYstem.IDisposable 接口来释放非托管的资源 C#中使用指针的语法 使用指针实现基于栈的高性能数组 值类型数据 程序第一 ...
- firefox ie 比较 relative path
relative path 对于firefox ie 来说是不同的 在ie中 <base href="/">起基础url作用 此时 <a href="& ...
- [HBase]region compaction流程
- vue 01 练习
1.有 红.黄.蓝 三个按钮,以及一个200x200矩形框box,点击不同的按钮,box的颜色会被切换为指定的颜色 <!DOCTYPE html> <html lang=" ...
- [Svelte 3] Use DOM events and event modifiers in Svelte 3
The whole magic of webapps is that users can interact with our code via various DOM events and Svelt ...
- off(events,[selector],[fn]) 在选择元素上移除一个或多个事件的事件处理函数。
off(events,[selector],[fn]) 概述 在选择元素上移除一个或多个事件的事件处理函数. off() 方法移除用.on()绑定的事件处理程序.有关详细信息,请参阅该网页上deleg ...
- 洛谷P2135 方块消除
洛谷题目链接 动态规划(真毒瘤!) 变量声明: $val[i]$:表示第$i$块颜色 $num[i]$:表示第$i$块颜色数量 $sum[i]$:表示$num$的前缀和 我们设计状态$f[l][r][ ...
- webpack给目录起别名
1. 配置文件目录: build>webpack.base.config.js: resolve: { alias: { '@': resolve('src'), 'styles': resol ...
- HDU 3081 Marriage Match II 最大流OR二分匹配
Marriage Match IIHDU - 3081 题目大意:每个女孩子可以和没有与她或者是她的朋友有过争吵的男孩子交男朋友,现在玩一个游戏,每一轮每个女孩子都要交一个新的男朋友,问最多可以玩多少 ...