第15章 泛型
泛型是在JDK5出现的,泛型实现了参数化类型的概念,使代码可以应用于多种类型。
15.2 简单泛型
促使泛型出现的原因之一就是创建容器类。
一个元组类库
元组:将一组对象直接打包存储于其中的一个单一对象,允许读取元素,不允许存放新元素。
@ToString
// 将两个对象打包于一个对象
class TwoTuple<A,B>{
public final A a;
public final B b;
public TwoTuple(A a, B b) {
this.a = a;
this.b = b;
}
}
/*
为什么要这么做?
通过声明public,客户端可以直接读取元素,声明final限制存放新元素,如果要存放新元素,需要重新创建对象
*/
// 通过继承实现更长的元组
@ToString
class ThreeTuple<A,B,C> extends TwoTuple<A,B>{
public final C c;
public ThreeTuple(A a, B b, C c) {
super(a, b);
this.c = c;
}
}
// 返回元组
public class Test {
public static void main(String[] args) {
System.out.println(f());
System.out.println(e());
}
static TwoTuple<String,Integer> f(){
return new TwoTuple<>("make",12);
}
static ThreeTuple<String,Integer,Integer> e(){
return new ThreeTuple<>("make",22,175);
}
}
一个栈类
class MyStack<T>{
// 节点类(包含当前数据及上一级节点)
private class Node<U>{
private U item;
private Node<U> next;
public Node(U item, Node<U> next) {
this.item = item;
this.next = next;
}
public boolean end(){
// 当数据和节点同时为空时及到头了
return item == null && next == null;
}
}
// 创建末端哨兵
private Node<T> top = new Node<>(null,null);
public void push(T item){
top = new Node<>(item,top);
}
public T pop(){
T item = top.item;
if (!top.end()){
top = top.next;
}
return item;
}
}
public static void main(String[] args) {
MyStack<Integer> stack = new MyStack<>();
for (int i = 0; i < 5; i++) {
stack.push(i);
}
for (int i = 0; i < 10; i++) {
System.out.println(stack.pop());
}
}
/*
4
3
2
1
0
null
null
null
null
null
*/
RandomList
class RandomList<T>{
private List<T> list = new ArrayList<>();
Random random = new Random();
public void add(T t){
list.add(t);
}
public T randomGet(){
return list.get(random.nextInt(list.size()));
}
}
public static void main(String[] args) {
RandomList<Integer> list = new RandomList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for (int i = 0; i < 5; i++) {
System.out.println(list.randomGet());
}
}
15.3 泛型接口
class Coffee {
// 计数,创建了多少对象
private static Integer counter = 0;
// 第n次创建的对象
private final Integer id = counter++;
@Override
public String toString() {
return "Coffee{" +
"id=" + id +
'}';
}
}
class ACoffee extends Coffee{}
class BCoffee extends Coffee{}
class CCoffee extends Coffee{}
interface Factory<T>{
T next();
}
class CoffeeFactory implements Factory<Coffee>,
// 为了可以使用foreach
Iterable<Coffee>{
private Class[] classes = {ACoffee.class,BCoffee.class,Coffee.class};
Random random = new Random();
private Integer size = 0;
public CoffeeFactory() {
}
// 如果要使用循环语句,必须有该方法,否则不知道何时停止
public CoffeeFactory(Integer size) {
this.size = size;
}
@SneakyThrows
@Override
public Coffee next() {
return (Coffee) classes[random.nextInt(classes.length)].newInstance();
}
@Override
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
// 迭代器
private class CoffeeIterator implements Iterator<Coffee>{
// 被迭代的数量
private Integer count = size;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public Coffee next() {
// 被迭代一次,减去一次
count--;
// 获得调用该迭代器的next()方法
return CoffeeFactory.this.next();
}
}
}
public static void main(String[] args) {
Factory<Coffee> factory = new CoffeeFactory();
for (int i = 0; i < 5; i++) {
System.out.println(factory.next());
}
for(Coffee coffee:new CoffeeFactory(5)){
System.out.println(coffee);
}
}
/*
Coffee{id=0}
Coffee{id=1}
Coffee{id=2}
Coffee{id=3}
Coffee{id=4}
Coffee{id=5}
Coffee{id=6}
Coffee{id=7}
Coffee{id=8}
Coffee{id=9}
*/
// 创建斐波那契数列
//此处可以看到泛型不支持基本类型,但是由于自动装箱/拆箱,使用中不存在问题
class FibonacciFactory implements Factory<Integer>{
private int num = 0;
@Override
public Integer next() {
return fib(num++);
}
private int fib(int n){
if (n < 2){
return 1;
}else {
return fib(n-2)+fib(n-1);
}
}
}
// 为了使用循环语句,这里使用适配器模式
class FibonacciFactoryAdapter extends FibonacciFactory implements Iterable<Integer>{
private int count =0;
public FibonacciFactoryAdapter() {
}
public FibonacciFactoryAdapter(int count) {
this.count = count;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int size = count;
@Override
public boolean hasNext() {
return size>0;
}
@Override
public Integer next() {
size--;
return FibonacciFactoryAdapter.this.next();
}
};
}
}
public static void main(String[] args) {
Factory<Integer> factory = new FibonacciFactory();
for (int i = 0; i < 5; i++) {
System.out.println(factory.next());
}
for (Integer num:new FibonacciFactoryAdapter(5)){
System.out.println(num);
}
}
/*
1
1
2
3
5
1
1
2
3
5
*/
15.4 泛型方法
在使用泛型方法即可解决问题时,不要使用泛型类。
static方法如果要使用泛型能力,必须使其成为泛型方法。
定义泛型方法:将泛型参数列表置于返回值之前。
public class Test {
public static void main(String[] args) {
f(1);
f("a");
f(11.2);
f(new StringBuilder());
}
static <T> void f(T t){
System.out.println(t.getClass().getSimpleName());
}
}
/*
Integer
String
Double
StringBuilder
*/
15.4.1 杠杆利用类型参数推断
本节主要是为了实现泛型的类型推断,该功能在JDK1.7中已经引入。
15.4.2 可变参数和泛型方法
public class Test {
public static void main(String[] args) {
f(1,2,3);
f("a","b","c");
}
static <T> void f(T ...args){
List<T> list = new ArrayList<>();
for (T arg : args) {
list.add(arg);
}
System.out.println(list);
}
}
/*
[1, 2, 3]
[a, b, c]
*/
15.4.3用于Factory的泛型方法
public class Test {
public static void main(String[] args) {
System.out.println(f(new ArrayList<>(), new CoffeeFactory(), 5));
}
static<T> Collection<T> f(Collection<T> collection,Factory<T> factory,int i){
for (int j = 0; j < i; j++) {
collection.add(factory.next());
}
return collection;
}
}
/*
[Coffee{id=0}, Coffee{id=1}, Coffee{id=2}, Coffee{id=3}, Coffee{id=4}]
*/
15.4.4通用的工厂方法
class BasicFactory<T> implements Factory<T>{
private Class<T> aClass;
public BasicFactory(Class<T> aClass) {
this.aClass = aClass;
}
@SneakyThrows
@Override
public T next() {
return aClass.newInstance();
}
}
public static void main(String[] args) {
Factory<Coffee> factory = new BasicFactory<>(Coffee.class);
for (int i = 0; i < 5; i++) {
System.out.println(factory.next());
}
}
15.4.5简化元组的使用
public class Test {
public static void main(String[] args) {
// 没有指定接收变量的参数类型,默认为Object
TwoTuple twoTuple = getTwoTuple(1,"a");
Integer i = (Integer) twoTuple.a;
ThreeTuple<String,Integer,Integer> threeTuple = getThreeTuple("make",12,111);
String name = threeTuple.a;
}
static <A,B> TwoTuple<A,B> getTwoTuple(A a,B b){
return new TwoTuple<>(a,b);
}
static <A,B,C> ThreeTuple<A,B,C> getThreeTuple(A a,B b,C c){
return new ThreeTuple<>(a,b,c);
}
}
15.4.6 一个Set实用工具
class Sets{
// a和b的所有元素
public static <T> Set<T> union(Set<T> a,Set<T> b){
Set<T> set = new HashSet<>(a);
set.addAll(b);
return set;
}
// a和b的交集
public static <T> Set<T> intersection(Set<T> a,Set<T> b){
Set<T> set = new HashSet<>(a);
set.retainAll(b);
return set;
}
// a中去除b中的元素
public static <T> Set<T> difference(Set<T> a,Set<T> b){
Set<T> set = new HashSet<>(a);
set.removeAll(b);
return set;
}
// a和b的非交集元素
public static <T> Set<T> complement(Set<T> a,Set<T> b){
return difference(union(a,b),intersection(a, b));
}
}
public static void main(String[] args) {
Set<String> a = new HashSet<>();
for (int i = 97; i < 101; i++) {
char c = (char) i;
a.add(String.valueOf(c));
}
System.out.println(a); // [a, b, c, d]
Set<String> b = new HashSet<>();
for (int i = 99; i < 103; i++) {
char c = (char) i;
b.add(String.valueOf(c));
}
System.out.println(b); // [c, d, e, f]
System.out.println(Sets.union(a,b)); // [a, b, c, d, e, f]
System.out.println(Sets.intersection(a,b)); // [c, d]
System.out.println(Sets.difference(a,b)); // [a, b]
System.out.println(Sets.complement(a,b)); // [a, b, e, f]
}
比较父子容器类间方法和接口的区别
class ContainerCom{
// 类方法集合
private static Set<String> methods(Class c){
Set<String> set = new TreeSet<>();
for (Method method : c.getMethods()) {
set.add(method.getName());
}
return set;
}
// 排除Object方法的类方法集合
public static Set<String> methodWithOutObject(Class c){
return Sets.difference(methods(c), methods(Object.class));
}
// 类接口集合
public static Set<String> interfaces(Class c){
Set<String> set = new TreeSet<>();
for (Class anInterface : c.getInterfaces()) {
set.add(anInterface.getSimpleName());
}
return set;
}
// 父类和子类方法/接口的区别
public static void diff(Class superClass,Class subClass){
System.out.println("方法区别:");
System.out.println(Sets.difference(methodWithOutObject(superClass),methodWithOutObject(subClass)));
System.out.println("接口区别:");
System.out.println(Sets.difference(interfaces(superClass),interfaces(subClass)));
}
}
public static void main(String[] args) {
ContainerCom.diff(List.class,ArrayList.class);
diff(Collection.class,List.class);
}
15.5 匿名内部类
class Person{
private Person(){}
public static Factory<Person> factory = new Factory<Person>() {
@Override
public Person next() {
return new Person();
}
};
public static Factory<Person> getFactory(){
return new Factory<Person>() {
@Override
public Person next() {
return new Person();
}
};
}
}
public static void main(String[] args) {
Person p = Person.factory.next();
p = Person.getFactory().next();
}
15.6 构建复杂模型
class Person<A,B,C>{}
class PersonList<A,B,C> extends ArrayList<Person<A,B,C>>{}
public static void main(String[] args) {
List<Person<String,Integer,Integer>> list = new PersonList<>();
list.add(new Person<>());
Person<String, Integer, Integer> person = list.get(0);
}
零售店模型
商店-走廊-货架-商品
// 商品
class Product{
private Integer id;
private Integer price;
private static Random random = new Random();
public Product(Integer id, Integer price) {
this.id = id;
this.price = price;
}
public static Factory<Product> getFactory(){
return new Factory<Product>() {
@Override
public Product next() {
int id = random.nextInt(10);
int price = random.nextInt(100);
return new Product(id,price);
}
};
}
@Override
public String toString() {
return new StringBuilder().append("商品编号:").append(id)
.append("商品价格:").append(price).toString();
}
}
// 货架
class Shelf extends ArrayList<Product>{
// 一条货架要包含多少商品
public Shelf(int productNum){
for (int j = 0; j < productNum; j++) {
add(Product.getFactory().next());
}
}
}
// 走廊
class Aisle extends ArrayList<Shelf>{
// 一条走廊要包含多上货架,每条货架上是多少商品
public Aisle(int shelfNum,int productNum){
for (int i = 0; i < shelfNum; i++) {
add(new Shelf(productNum));
}
}
}
// 商店
class Store extends ArrayList<Aisle> {
public Store(int aisleNum,int shelfNum,int productNum){
for (int i = 0; i < aisleNum; i++) {
add(new Aisle(shelfNum,productNum));
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Aisle aisle : this) {
for (Shelf shelf : aisle) {
for (Product product : shelf) {
sb.append(product).append("\r\n");
}
}
}
return sb.toString();
}
}
public static void main(String[] args) {
Store store = new Store(1,2,3);
System.out.println(store);
}
/*
商品编号:8商品价格:17
商品编号:1商品价格:39
商品编号:4商品价格:48
商品编号:0商品价格:91
商品编号:1商品价格:5
商品编号:2商品价格:37
*/
15.7 擦除的神秘之处
class A<T>{}
class B<T,U>{}
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
List<String> stringList = new ArrayList<>();
// 在运行时类型会被擦除,他们都是List
System.out.println(integerList.getClass() == stringList.getClass()); // true
A<Integer> a = new A<>();
B<A<Integer>,String> b= new B<>();
// getTypeParameters()返回泛型参数,但是并不是具体的类型参数,并没有实际意义
System.out.println(Arrays.toString(a.getClass().getTypeParameters())); // [T]
System.out.println(Arrays.toString(b.getClass().getTypeParameters())); // [T, U]
}
15.7.1 C++的方式
翻译为Java的版本:
class HasF{
public void f(){
System.out.println("this is F");
}
}
class Manipulator<T extends HasF>{
private T obj;
public Manipulator(T obj) {
this.obj = obj;
}
public void manipulate(){
// 类型擦除会擦除到它的上一个边界(可能会有多个边界)
/*
可以看到,这样使用泛型和使用HasF没有什么区别,
如果是在类内部使用,就没必要使用泛型,
在外部类中会使用该类型,才有必要使用泛型
*/
obj.f();
}
}
15.7.2 迁移兼容性
为了保持兼容性,泛型在运行时会被擦除。
15.7.3 擦除的问题
擦除也存在明显的代价,泛型不能用于显示地运行时类型操作,比如转型/instanceof/new,因为所有关于参数的类型信息都丢失了。
使用泛型并不是强制的。
public class Test {
public static void main(String[] args) {
A a = new A();
a.setT(1);
Object o = a.getT();
A<String> stringA = new A<>();
stringA.setT("a");
String t = stringA.getT();
}
}
class A<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
15.7.4 边界的动作
class arrayMaker<T>{
private Class<T> kind;
public arrayMaker(Class<T> kind) {
this.kind = kind;
}
@SuppressWarnings("unchecked")
public T[] create(int size){
// newInstance()方法返回Object对象,需要转型为T[],因为T是泛型,不存在类型信息,所以会产生警告信息
return (T[]) Array.newInstance(kind,size);
}
}
public class Test {
public static void main(String[] args) {
arrayMaker<String> arrayMaker = new arrayMaker<>(String.class);
String[] strings = arrayMaker.create(5);
System.out.println(Arrays.toString(strings));
}
}
class ListMaker<T>{
List<T> create(){
return new ArrayList<>();
}
// 这里会产生警告
@SuppressWarnings("unchecked")
List<T> create2(){
return new ArrayList();
}
}
边界: 对象进入和离开方法的地点。
泛型的动作发生在边界处,对传递进来的值机型额外检查,对传递出去的值转型。
15.8 擦除的补偿
擦除丢失了泛型的类型信息,为了使用类型信息,需要显示的传递类型信息。
class A{}
class B extends A{}
class ClassType<T>{
private Class<T> kind;
// 显示传递类型信息
public ClassType(Class<T> kind) {
this.kind = kind;
}
public boolean is(Object obj){
return kind.isInstance(obj);
}
}
public static void main(String[] args) {
ClassType<B> classType = new ClassType<>(B.class);
System.out.println(classType.is(new A())); // false
System.out.println(classType.is(new B())); // true
}
15.8.1 创建类型实例
内建的工厂对象
class ClassAsFactory<T>{
public T t;
public ClassAsFactory(Class<T> kind) {
try {
t = kind.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ClassAsFactory<String> classAsFactory = new ClassAsFactory<>(String.class);
// 遇到无默认构造器的类会报错
// ClassAsFactory<Integer> integerClassAsFactory = new ClassAsFactory<>(Integer.class);
}
显式的工厂对象
interface Factory<T>{
T create();
}
// 具体对象
class A{
public static class AFactory implements Factory<A>{
@Override
public A create() {
return new A();
}
}
}
// 可接纳传入的对象
class Need<T>{
public T t;
// 注意:两个泛型必须对上,F代表的是工厂对象,而该工厂对象创建的对象是T
public <F extends Factory<T>>Need(F f){
t = f.create();
}
}
public static void main(String[] args) {
Need<A> need = new Need<>(new A.AFactory());
}
模板方法设计模式
abstract class Factory<T>{
public T create(){
return real();
}
protected abstract T real();
}
class AFactory extends Factory<String>{
@Override
protected String real() {
return "a";
}
}
public static void main(String[] args) {
Factory<String> factory = new AFactory();
String s = factory.create();
}
15.8.2 泛型数组
public class Test {
static A<Integer>[] as;
@SuppressWarnings("unchecked")
public static void main(String[] args) {
// 并不能以这样的形势创建
// as = new A<Integer>[5];
// 存在警告
// 该数组在运行时依旧是Object数组
// 创建泛型数组的方式就是创建一个被擦除类型的新数组,然后对其转型
as = (A<Integer>[]) new A[5];
as[0] = new A<>();
}
}
class A<T>{}
泛型数组包装器
class FactoryArray<T>{
private T[] ts;
@SuppressWarnings("unchecked")
public FactoryArray(int size){
ts = (T[]) new Object[size];
}
public T get(int index){
return ts[index];
}
public void set(int index,T t){
ts[index] = t;
}
public T[] rep(){
return ts;
}
}
public static void main(String[] args) {
FactoryArray<Integer> factoryArray = new FactoryArray<>(5);
factoryArray.set(0,1);
Integer integer = factoryArray.get(0);
/*
数组将跟踪它们的实际类型,而这个类型是数组被创建时确定的,
即时已被转型为Integer[],但这只存在于编译期,
实际上它还是Object[]类型
*/
// Integer[] integers = factoryArray.rep();
Object[] objects = factoryArray.rep();
}
另一种实现方式:内部使用Object[]储存
class FactoryArray<T>{
private Object[] objects;
@SuppressWarnings("unchecked")
public FactoryArray(int size){
objects = new Object[size];
}
public T get(int index){
return (T) objects[index];
}
public void set(int index,T t){
objects[index] = t;
}
public T[] rep(){
return (T[]) objects;
}
}
public static void main(String[] args) {
FactoryArray<Integer> factoryArray = new FactoryArray<>(5);
factoryArray.set(0,1);
Integer integer = factoryArray.get(0);
// 无论何种方式都不能推翻底层的数组类型
// 该种方式的好处是使我们认识到无论何时底层都是Object[]类型
// Integer[] rep = factoryArray.rep();
Object[] rep = factoryArray.rep();
}
获得确切类型的数组
class FactoryArray<T>{
private T[] ts;
@SuppressWarnings("unchecked")
// 通过传入类型,来弥补擦除的类型
public FactoryArray(Class<T> tClass,int size){
ts = (T[]) Array.newInstance(tClass,size);
}
public T get(int index){
return ts[index];
}
public void set(int index,T t){
ts[index] = t;
}
public T[] rep(){
return ts;
}
}
public static void main(String[] args) {
FactoryArray<Integer> factoryArray = new FactoryArray<>(Integer.class,5);
// 这里会获得确切类型的数组
Integer[] rep = factoryArray.rep();
}
15.9 边界
边界可以用于泛型的参数类型上设置限制条件。
interface A{}
class B implements A{}
interface C extends A{}
class D{}
interface E{}
// 限定泛型的边界为A
class X<T extends A>{}
// T可以为多个的任意一个子类或实现类,第一个可以是类或接口,其他必须是接口
class Z<T extends D & A & E>{}
// T可以为任意A接口的继承接口或实现类的子实现类或子类,因为X已经限定了边界为A;
// 如果希望子类的泛型允许是多个的子类,则基类的泛型必须是接口,否则不符合上边的原理
class Y<T extends B & C> extends X<T>{}
15.10 通配符
数组向上转型存在的问题
class Fruit{}
class Apple extends Fruit{}
class BingApple extends Apple{}
class Orange extends Fruit{}
public static void main(String[] args) {
Fruit[] fruits = new Apple[5];
fruits[0] = new Apple();
fruits[1] = new BingApple();
fruits[2] = new Orange(); // 抛出异常:ArrayStoreException
}
/*
在编译期该数组向上转型是允许的,但是数组储存的就是其本来的类型,因此,在向Apple[]数组存入Orange[]数组时会出现异常
*/
尝试解决数组问题的一种方案
public static void main(String[] args) {
/*
该种写法是不允许的,实际上,这不是向上转型,
尽管Apple是Fruit的子类型,但是持有Apple类型的List
不是持有Fruit类型的List的子类型。(可以这样理解,Fruit里包含
的不仅仅是Apple类型,当存入Orange类型时,ArrayList就不能储存该
类型;自动转型转的是List,不能把List中存的对象也转型了)
*/
// List<Fruit> list = new ArrayList<Apple>();
}
解决向上转型问题
public static void main(String[] args) {
/*
该种形式是允许向上转型的,<? extends Fruit>代表的意思是
这是Fruit的子类型,但是我不知道是哪个子类型。
虽然向上转型成功了,但是并不能向里面放入Apple对象,因为
其并不知道是哪个子类型。
放入Fruit对象也不可以,因为里面存入的是Fruit的子类型,存
Fruit类型相当于向下转型,因此是不可以的。
但是,我们可以get一个Fruit对象,因为容器中的对象肯定是
Fruit类型
*/
List<? extends Fruit> list = new ArrayList<Apple>();
// list.add(new Apple());
// list.add(new Fruit());
}
15.10.1 编译器有多聪明
public static void main(String[] args) {
List<? extends Fruit> list = new ArrayList<Apple>();
Apple apple = (Apple) list.get(0);
int index = list.indexOf(new Apple());
boolean flag = list.contains(new Apple());
}
/*
可以看到indexOf()等方法是可以接收各种类型的,为什么呢,因为他们的参数类型不是泛型,而是Object
*/
模拟上述案例
class Holder<T>{
private T t;
public Holder(T t){
this.t = t;
}
public T get(){
return t;
}
public void set(T t){
this.t = t;
}
public boolean equals(Object obj){
return t.equals(obj);
}
}
public static void main(String[] args) {
Holder<Apple> holder = new Holder<>(new Apple());
Apple apple = holder.get();
holder.set(new Apple());
holder.equals(new Object());
Holder<? extends Fruit> holder1 = holder;
Fruit fruit = holder1.get();
// holder1.set(new Apple()); // 不允许
holder1.equals(new Object());
}
15.10.2 逆变
超类型通配符<? super MyClass>或<? super T>。
public static void main(String[] args) {
List<? super Apple> list = new ArrayList<Apple>();
list.add(new Apple());
list.add(new BingApple());
}
总结:
<? extends MyClass>表达的含义是继承基类的某个子类,强调的含义是子类;<? super MyClass>表达的含义是基类的子类,强调的含义是基类;前者并不知道是哪个子类,因此,并不能向容器中添加对象,而后者知道基类,基类就一个,肯定就是这个基类,因此可以添加基类及子类的对象。
class Holder{
static <T> void a(List<T> list,T t){
list.add(t);
}
static <T>void b(List<? super T> list,T t){
list.add(t);
}
public static void f1(){
a(new ArrayList<Apple>(),new Apple());
a(new ArrayList<Fruit>(),new Apple());
}
public static void f2(){
b(new ArrayList<Apple>(),new Apple());
b(new ArrayList<Fruit>(),new Apple());
}
}
/*
书上说在f1()方法中,第二行代码不可以,在实验中是可以的
*/
15.10.3 无界通配符
<?>表示使用泛型编码,而不是使用原生类型。
class A{
List list1;
List<?> list2;
List<? extends Object> list3;
void f1(List list){
list1 = list;
list2 = list;
list3 = list; // 会有警告
}
void f2(List<?> list){
list1 = list;
list2 = list;
list3 = list;
}
void f3(List<? extends Object> list){
list1 = list;
list2 = list;
list3 = list;
}
}
/*
总体看,并无区别
*/
多个泛型参数,需要一个参数确定,而其他不确定时
class A{
Map map1;
Map<String,?> map2;
Map<?,?> map3;
void f(Map map){
map1 = map;
// 有警告
// 为什么?map的key可能是任意类型,而map2的key是String类型,因此,可能出错
map2 = map;
map3 = map;
}
}
更加详细的使用
class WildCards{
static void rawArgs(Holder holder,Object args){
/*
为什么会有警告?
Holder是一个泛型类型,这里被表示为原生类型;只要使用了原生类型,都会放弃编译期检查,因此是不安全的
*/
holder.set(args); // unchecked警告
holder.set(new WildCards()); // unchecked警告
Object o = holder.get();
}
static void unboundArgs(Holder<?> holder, Object args){
/*
和上面的例子做对比,大部分人认为,这里会报警告而不是错误。
为什么会出现错误?因为Holder表示包含任意类型,而Holder<?>表示某种具体类型,
但是我们不知道这种类型是什么。
*/
// holder.set(args); // Error
Object o = holder.get();
}
static <T> T exact1(Holder<T> holder,T t){
holder.set(t);
return holder.get();
}
static <T> T exact2(Holder<? extends T> holder,T t){
// holder.set(t); // Error
return holder.get();
}
static <T> void exact3(Holder<? super T> holder,T t){
holder.set(t);
// 该类的超类并不仅仅是一个
// T t1 = holder.get(); // Error
// 因此,只有Object类型才是安全的
Object object = holder.get();
}
// 对exact3的解释
public static void main(String[] args) {
List<? super Apple> list = new ArrayList<>();
List<? super Fruit> list1 = new ArrayList<>();
list = list1;
}
}
15.10.4 捕获转换
向<?>参数传递原生类型参数,其会捕获原生类型的具体类型,从而可以向需要传递具体类型的需求传递该类型。
public class Test {
public static void main(String[] args) {
Holder<Integer> integerHolder = new Holder<>(1);
f2(integerHolder);
Holder holder = new Holder("a");
f2(holder);
Holder<?> wildCardHolder = new Holder<>(1.0);
f2(wildCardHolder);
}
static <T>void f1(Holder<T> holder){
System.out.println(holder.get().getClass().getSimpleName());
}
static void f2(Holder<?> holder){
f1(holder);
}
}
/*
如上,f2()先接收原生类型,再向f1()传递包含具体类型的参数。
但是,实际上直接调用f1()也是同样的效果。
可能是作者写书是用的JDK5,而在JDK7中已经有了类型推断这一功能。
*/
总结
可以看到,很多通过符组合,表示的意思是,其代表某种意思,但是我们不知道这个意思是什么,其大部分用途是在方法的形参上,让它代表这个意思,然后通过实参,传入具体的类型。
15.11 问题
基类类型不能用作泛型参数
public static void main(String[] args) {
// List<int> list = new ArrayList<>();
// 使用包装类型代替
List<Integer> list = new ArrayList<>();
}
public class Test {
public static void main(String[] args) {
// 自动包装机制局限:在数组中无法使用
// int[] fs = f(new int[2]);
// int[] fs = f(new Integer[2]);
Integer[] fs = f(new Integer[3]);
}
static <T> T[] f(T[] ts){
return ts;
}
}
实现参数化接口
一个类不能实现同一个泛型接口的两种变体。
interface A<T>{}
class B implements A<String>{}
// 编译不能通过
// class C extends B implements A<Integer>{}
如下写法可以
interface A<T>{}
class B implements A{}
class C extends B implements A{}
转型和警告
class A<T>{
int index = 0;
Object[] objects;
A(int size){
objects = new Object[size];
}
void push(T t){
objects[index++] = t;
}
T pop(){
// 警告
// 因为泛型会被擦除,所以编译器无法知道转型是否安全
return (T) objects[index--];
}
}
有时泛型并不能消除转型需求
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(null);
// readObject()必须转型,但是它也不知道在读取什么
// 泛型并没有消除转型需求
List<String> list = (List<String>) inputStream.readObject();
// 引入SE5的转型方式,但是仍然会产生警告(ps:那这有什么用。。。)
list = (List<String>) List.class.cast(inputStream.readObject());
}
重载
class A<W, T> {
void f(List<W> list) {}
void f(List<T> list){}
}
/*
由于泛型擦除,上述重载方法会产生相同的类型签名
*/
基类劫持了接口
class Pet implements Comparable<Pet>{
@Override
public int compareTo(Pet o) {
return 0;
}
}
// 一旦为接口确定了泛型参数,子类就不能做改变
// class SubPet extends Pet implements Comparable<SubPet>{}
class OtherPet extends Pet implements Comparable<Pet>{}
15.12 自限定的类型
自限定
class SelfBounded<T extends SelfBounded<T>>{
private T t;
public T get(){
return t;
}
public void set(T t){
this.t = t;
}
@Override
public String toString() {
return t.getClass().getSimpleName();
}
}
class A extends SelfBounded<A>{}
class C extends SelfBounded<A>{}
// 也可以用于方法
static <T extends SelfBounded<T>> T f(T t){
return t;
}
public static void main(String[] args) {
A a = new A();
a.set(a);
A a1 = a.get();
System.out.println(a);
C c = new C();
c.set(a1);
}
/*
可以限定子类的泛型参数必须是SelfBounded的子类型
*/
疑问:
class SelfBound<T extends SelfBound>{
private T t;
public T get(){
return t;
}
public void set(T t){
this.t = t;
}
@Override
public String toString() {
return t.getClass().getSimpleName();
}
}
/*
如上写法也是可以达到相同效果的,这两种有什么区别
*/
参数协变
自限定类型的价值在于它们可以产生协变参数类型——方法参数类型会随子类而变化。
返回类型:
class Base{}
class Drived extends Base{}
interface OrdinaryGatter{
Base get();
}
interface DrivedGatter extends OrdinaryGatter{
// 返回具体类的接口,需要重写接口
@Override
Drived get();
}
interface OrdinaryGatter<T extends OrdinaryGatter<T>>{
T get();
}
// 使用自限定类型不需要重写接口
interface DrivedGatter extends OrdinaryGatter<DrivedGatter>{
}
public static void main(String[] args) {
DrivedGatter drivedGatter = null;
drivedGatter.get();
}
参数:
class OrdinarySatter{
void set(Base base){}
}
class DrivedSatter extends OrdinarySatter{
// 无法编译
@Override // 注释后,会重载,产生两个set方法
void set(Drived base) {}
}
// 使用自限定后可以让子类参数产生变化
interface OrdinarySatter<T extends OrdinarySatter<T>>{
void set(T t);
}
interface DrivedSatter extends OrdinarySatter<DrivedSatter>{
}
static void f(DrivedSatter satter){
satter.set(satter);
}
15.13 动态类型安全
由于以前代码不存在泛型机制,Java工具类Collections中提供了一系列的工具。
public class Test {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Dog());
fCat(list);
List<Dog> dogList = Collections.checkedList(new ArrayList<>(), Dog.class);
dogList.add(new Dog());
fCat(dogList); // ClassCastException
List<Pet> petList = Collections.checkedList(new ArrayList<>(), Pet.class);
petList.add(new Dog());
petList.add(new Cat());
fCat(petList);
}
static void fCat(List list){
list.add(new Cat());
}
}
15.14 异常
抛出泛型异常
class Failure extends Exception{}
interface Process<E extends Exception>{
void f() throws E;
}
class RunProcess implements Process<Failure>{
@Override
public void f() throws Failure {
throw new Failure();
}
<F extends Exception> void e(){
// 并不允许catch泛型异常
/*
try {
}catch (F f){
}
*/
}
}
15.15 混型
混型 最基本的概念是混合多个类型的能力,以产生一个表示混型中所有类的能力。
混型的价值之一就是可以将特性和行为一致地应用于多个类之上;如果在混型类中修改某些东西,这些修改会应用于混型所应用的所有类型之上。
15.15.2 与接口混合
// 时间戳接口
interface TimeStamped{
long getStamp();
}
class TimeStampedImpl implements TimeStamped{
@Override
public long getStamp() {
return new Date().getTime();
}
}
// 序列号
interface Number{
int getNumber();
}
class NUmberImpl implements Number{
private static int i = 0;
@Override
public int getNumber(){
return ++i;
}
}
class Basic{
private String val;
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
}
// 混合类
// 实现相应的接口
class Mixin extends Basic implements TimeStamped,Number{
// 每个混入类型都需要有相应的域
private TimeStamped timeStamped = new TimeStampedImpl();
private Number number = new NUmberImpl();
@Override
public long getStamp() {
// 将功能交由具体类实现
return timeStamped.getStamp();
}
@Override
public int getNumber() {
return number.getNumber();
}
}
public static void main(String[] args) {
Mixin mixin = new Mixin();
mixin.setVal("一混型");
System.out.println(new StringBuilder(mixin.getVal())
.append(" ")
.append(mixin.getNumber())
.append(" ")
.append(mixin.getStamp())); // 一混型 1 1604302754656
}
15.15.3 使用装饰器模式
装饰器模式使用分层对象来动态透明地向单个对象中添加责任,装饰器类和被装饰者有相同的接口。装饰器经常用于满足各种可能的组合,但是直接子类化,会产生过多的类。
// 装饰器类
class Decorator extends Basic{
private Basic basic;
public Decorator(Basic basic) {
this.basic = basic;
}
@Override
public String getVal() {
return super.getVal();
}
@Override
public void setVal(String val) {
super.setVal(val);
}
}
class NUmberImpl extends Decorator{
private static int i = 0;
public NUmberImpl(Basic basic) {
super(basic);
}
// 可以添加自己的方法,但是具有局限性
public int getNumber(){
return ++i;
}
}
class TimeStampedImpl extends Decorator{
public TimeStampedImpl(Basic basic) {
super(basic);
}
public long getStamp() {
return new Date().getTime();
}
}
public static void main(String[] args) {
NUmberImpl nUmber = new NUmberImpl(new Basic());
TimeStampedImpl timeStamped = new TimeStampedImpl(nUmber);
}
15.15.4 与动态代理混合
class MixinProxy implements InvocationHandler{
private Map<String,Object> map;
// 要代理的对象
// 由于动态代理的限制,每个要混入的类必须是某个接口的实现
public MixinProxy(/*代理对象和对应Class*/Map<Object,Class> maps) throws IllegalAccessException, InstantiationException {
map = new HashMap<>();
for(Map.Entry<Object,Class> entry:maps.entrySet()){
for (Method method : entry.getValue().getMethods()) {
// 将方法名与对象绑定
map.put(method.getName(),entry.getKey());
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
return method.invoke(map.get(name),args);
}
// 创建实例
public static Object newInstance(Map<Object,Class> maps) throws InstantiationException, IllegalAccessException {
MixinProxy mixinProxy = new MixinProxy(maps);
List<Class> list = new ArrayList<>();
for(Map.Entry<Object,Class> entry:maps.entrySet()){
list.add(entry.getValue());
}
Class[] interfaces = new Class[list.size()];
// List转数组
list.toArray(interfaces);
return Proxy.newProxyInstance(list.get(0).getClassLoader(),interfaces ,mixinProxy);
}
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Map<Object,Class> map = new HashMap<>();
map.put(new TimeStampedImpl(),TimeStamped.class);
map.put(new NUmberImpl(),Number.class);
Object o = MixinProxy.newInstance(map);
// 缺点是必须强转为指定的类型
TimeStamped timeStamped = (TimeStamped) o;
System.out.println(timeStamped.getStamp()); // 1604306931322
Number number = (Number) o;
System.out.println(number.getNumber()); // 1
}
15.16 潜在类型机制
编程要具有的思想:编写尽可能广泛地应用的代码,为了实现这一点,我们需要各种途径放松代码对类型所做的限制,同时不丢失静态类型检查的好处。
但是Java中当要在泛型上执行操作时,擦除会要求擦除到泛型的边界,这就对泛型的使用做了限制。
某些其他语言提供了一种潜在类型机制,只需要实现某个方法的子集,而不是特定的类或接口。
潜在类型机制是一种代码组织和复用机制,有了它编写的代码更加容易复用。
python示例:
class Dog:
def speak(self):
print "Arf"
def sit(self):
print "Sitting"
class Robot:
def speak(self):
print "Click"
def sit(self):
print "Clank"
def perform(anything):
anything.speak()
anything.sit()
a = Dog()
b = Robot()
perform(a)
perform(b)
# 可以看到Dog和Robot并没有什么关系,除了他们有两个同名方法。
15.17 对缺乏潜在类型机制的补偿
尽管Java不支持泛型机制,但仍然可以创建真正的泛型代码
反射
class Mime{
public void sit(){
System.out.println("我坐下");
}
@Override
public String toString() {
return "我";
}
}
class Dog{
public void sit(){
System.out.println("狗坐下");
}
public void speak(){
System.out.println("狗叫");
}
}
public class Test2 {
public static void perform(Object o){
Class<?> aClass = o.getClass();
try {
Method sit = aClass.getMethod("sit");
sit.invoke(o);
} catch (Exception e) {
// 同时可以处理异常
System.out.println(o+"不能坐");
}
try {
Method speak = aClass.getMethod("speak");
speak.invoke(o);
} catch (Exception e) {
System.out.println(o+"不能说");
}
}
public static void main(String[] args) {
perform(new Mime());
perform(new Dog());
}
}
将一个方法应用于序列
实现编译器检查和潜在类型机制
将任意方法应用于对象的集合。
class Shape{
public void dis(){
System.out.println("未固定形状");
}
}
class Round extends Shape{
@Override
public void dis() {
System.out.println("圆形");
}
}
public class Test2 {
public static void main(String[] args) throws NoSuchMethodException {
List<Shape> shapeList = new ArrayList<>();
shapeList.add(new Shape());
shapeList.add(new Round());
Random random = new Random();
apply(shapeList,shapeList.get(0).getClass().getMethod("dis"));
}
// T代表集合中的对象,S代表集合
// 泛型中的意思:S必须继承Iterable(代表其是集合),后者的泛型代表,集合中存入的元素
// 是T及其子类
public static <T,S extends Iterable<? extends T>>void apply(S list,Method f,Object...args){
try {
for (T o:list){
f.invoke(o,args);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
当你并未碰巧拥有正确的接口时
public class Test2 {
public static void main(String[] args) throws NoSuchMethodException {
apply(new ArrayList<>(),Shape.class,3);
}
// 该方法并不能接收MyList,尽管其也有add()方法
// 这证明了潜在类型的价值,Java没有该机制,所以会受到特定类型的限制l
public static <T>void apply(List<T> list,Class<? extends T> c,int num){
try {
for (int i = 0; i < num; i++) {
list.add(c.newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyList<T>{
public void add(T t){}
}
用适配器仿真潜在类型机制
// 接口
interface Addable<T>{
void add(T t);
}
class Fill2{
// 仅接受Addable
public static <T> void fill(Addable<T> addable,Class<T> c,int num){
try {
for (int i = 0; i < num; i++) {
addable.add(c.newInstance());
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
// 适配器:将集合适配Addable
class AddableCollectionAdapter<T> implements Addable<T>{
private Collection<T> collection;
public AddableCollectionAdapter(Collection<T> collection) {
this.collection = collection;
}
@Override
public void add(T t) {
collection.add(t);
}
}
public static void main(String[] args) throws NoSuchMethodException {
Fill2.fill(new AddableCollectionAdapter<>(new ArrayList<>()),Shape.class,2);
}
15.18 将函数对象用作策略
主要介绍了下策略模式的应用;
// 操作两个数
interface Combiner<T> {
T combine(T x, T y);
}
// 一元函数
interface UnaryFunction<R,T>{
R function(T t);
}
// 在一元函数基础上增加最终结果的接口
interface Collector<T> extends UnaryFunction<T,T>{
T result();
}
// 一元谓词
interface UnaryPredicate<T>{
boolean test(T t);
}
class IntegerAdder implements Combiner<Integer>{
@Override
public Integer combine(Integer x, Integer y) {
return x+y;
}
}
class IntegerSub implements Combiner<Integer>{
@Override
public Integer combine(Integer x, Integer y) {
return x - y;
}
}
class BigIntegerAdder implements Combiner<BigInteger>{
@Override
public BigInteger combine(BigInteger x, BigInteger y) {
return x.add(y);
}
}
class BigDecimalUlp implements UnaryFunction<BigDecimal,BigDecimal>{
@Override
public BigDecimal function(BigDecimal bigDecimal) {
// 返回一个数字和距其最近的数字之间的距离
return bigDecimal.ulp();
}
}
// 实现比较
class GreaterThan</*实现比较接口*/T extends Comparable<T>> implements UnaryPredicate<T>{
private T bound;
public GreaterThan(T bound) {
this.bound = bound;
}
@Override
public boolean test(T x) {
return x.compareTo(bound)>0;
}
}
class MultIntegerCollector implements Collector<Integer>{
private Integer val = 1;
@Override
public Integer function(Integer integer) {
val *= integer;
return val;
}
@Override
public Integer result() {
return val;
}
}
// 函数处理类
class Functional{
public static void main(String[] args) throws NoSuchMethodException, ScriptException {
// 执行函数,获得结果
List<Integer> integerList = Arrays.asList(1,2,3,4,5);
Integer i = reduce(integerList,new IntegerAdder());
System.out.println(i); // 累加 15
i = reduce(integerList,new IntegerSub());
System.out.println(i); // 累减 -13
List<BigInteger> bigIntegerList = Arrays.asList(
new BigInteger("1"),new BigInteger("2"),
new BigInteger("3"));
BigInteger bigInteger = reduce(bigIntegerList, new BigIntegerAdder());
System.out.println(bigInteger); // 大整数累加 6
// 获得函数处理后的结果
Collector<Integer> collector = forEach(integerList, new MultIntegerCollector());
System.out.println(collector.result()); // 120
// 将集合执行后的结果存入List中返回
List<BigDecimal> bigDecimalList = Arrays.asList(
new BigDecimal("1"),new BigDecimal("2"),
new BigDecimal("3"));
List<BigDecimal> transform = transform(bigDecimalList, new BigDecimalUlp());
System.out.println(transform); // [1, 1, 1]
// 返回符合结果的集合
List<BigInteger> filter = filter(bigIntegerList, new GreaterThan<>(new BigInteger("2")));
System.out.println(filter); // [3]
}
// 累计处理获得结果
public static <T> T reduce(/*可迭代的集合*/Iterable<T> seq,Combiner<T> combiner){
// 获得迭代器
Iterator<T> iterator = seq.iterator();
// 为什么不直接使用while?combine()方法需要两个参数,
if (iterator.hasNext()){
T result = iterator.next();
while (iterator.hasNext()){
result = combiner.combine(result,iterator.next());
}
return result;
}
return null;
}
// 执行一元函数
// 通过接口定义参数,这里用到了策略模式,可以传入不同具体的实现
public static <T> Collector<T> forEach(Iterable<T> seq,Collector<T> func){
for (T t : seq) {
func.function(t);
}
return func;
}
// 将集合执行后的结果存入List中返回
public static <R,T> List<R> transform(Iterable<T> seq,UnaryFunction<R,T> func){
List<R> result = new ArrayList<>();
for (T t : seq) {
result.add(func.function(t));
}
return result;
}
// 将集合中符合规则的元素存入List
public static <T> List<T> filter(Iterable<T> seq,UnaryPredicate<T> pred){
List<T> result = new ArrayList<>();
for (T t : seq) {
if (pred.test(t)){
result.add(t);
}
}
return result;
}
}
15.19 总结
- 泛型最大的价值体现在容器类的使用中;
- 在容器中获得Object对象并转型,出现错误并无法发现并不常见;
- 将容器对象合理命名可以很好的避免这种情况;
- 如此显著和复杂的特性(泛型)并非仅仅为了解决转型的错误;
- 泛型更重要的目的在于表达性;
- 正如泛型的名字,目的是创建出更通用的代码。