泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。... Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 称之为泛型(generics);ML、Scala 和 Haskell 称之为参数多态(parametric polymorphism);C++ 和 D称之为模板。《Design Patterns》一书称之为参数化类型(parameterized type)。
https://zh.wikipedia.org/wiki/泛型
Position第一版
Position
public class Position { private Object sth; public Position(Object sth) { this.sth = sth; } Object get() { return sth; } }
Position第二版
public class Position { private Creature sth; public Position(Creature sth) { this.sth = sth; } Creature get() { return sth; } }
public class Position { private Creature sth; }
public class Position<T>{ private T sth; }
public class Position<T extends Creature>{ private T sth; }
class Stack{ void push(Object o){...} Object pop(){...} } String s = "Hello"; Stack st = new Stack(); ... st.push(s); ... s = (String)st.pop();
class Stack<A>{ // 类参数 void push(A a){...} A pop(){...} } String s = "Hello"; Stack<String> st = new Stack<String>(); st.push(s); ... s = st.pop();
public class Computer{ private HDD mHarddisk; // 机械硬盘 Computer(HDD harddisk){ mHarddisk = harddisk; } public Data readDisk(){ return mHarddisk.read(); } public void writeDisk(Data data){ mHarddisk.write(data); } }
public class SSDComputer{ private SSD mHarddisk; // SSD硬盘 Computer(SSD harddisk){ mHarddisk = harddisk; } public Data readDisk(){ return mHarddisk.read(); } public void writeDisk(Data data){ mHarddisk.write(data); } }
public abstract class Disk{}; public class SSD extends Disk{}; public class HHD extends Disk{}; public class Computer{ private Disk disk; // 抽象的硬盘 Computer(Disk disk){ this.disk = disk; } public Data readDisk(){ return disk.read(); } public void writeDisk(Data data){ disk.write(data); } public Disk getDisk(){ return disk; } }
public class Computer<T extends Disk>{ private T disk; // 参数类 Computer(T disk){ this.disk = disk; } public Data readDisk(){ return disk.read(); } public void writeDisk(Data data){ disk.write(data); } public T getDisk(){ return disk; } public void setDisk(T disk){ this.disk = disk; } public static void main(String[] args) { Computer<SSD> computer = new Computer<SSD>(new SSD()); SSD disk = computer.getDisk(); // No cast needed //computer.setDisk(new HHD()); // error! } }
public class GenericMethods { public <T> void f(T x) { System.out.println(x.getClass().getName()); } public static void main(String[] args) { GenericMethods gm = new GenericMethods(); gm.f(""); gm.f(1); //autoboxing gm.f(1.0); gm.f(1.0F); gm.f(‘c’); gm.f(gm); } }
public class Holder<T> { private T obj; public void set(T obj){ this.obj = obj; } public T get(){ return obj; } public static void main(String[] args){ Holder<Integer> holder = new Holder<>(); holder.set(1); //holder.set("Abc"); // error Integer obj = holder.get(); //无须cast } }
javap -v -p -s -sysinfo -constants Holder.class
Constant pool: #1 = Methodref #9.#29 // java/lang/Object."<init>":()V #2 = Fieldref #3.#30 // Holder.obj:Ljava/lang/Object; #3 = Class #31 // Holder #4 = Methodref #3.#29 // Holder."<init>":()V #5 = Methodref #8.#32 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #6 = Methodref #3.#33 // Holder.set:(Ljava/lang/Object;)V #7 = Methodref #3.#34 // Holder.get:()Ljava/lang/Object; #8 = Class #35 // java/lang/Integer #9 = Class #36 // java/lang/Object ... #29 = NameAndType #14:#15 // "<init>":()V #30 = NameAndType #10:#11 // obj:Ljava/lang/Object; #31 = Utf8 Holder #32 = NameAndType #37:#38 // valueOf:(I)Ljava/lang/Integer; #33 = NameAndType #18:#19 // set:(Ljava/lang/Object;)V #34 = NameAndType #21:#22 // get:()Ljava/lang/Object;
public class Computer<T extends Disk> { private T disk; // 运行时disk是Disk类型 Computer(T disk) { disk = disk; } public void setDisk(T disk) { this.disk = disk; } } class Disk {};
Java泛型的实现方式是将类型参数用边界类型替换,在上面的例子中就是把T用Disk替换。这种实现方式看上去就像是把具体的类型擦除到了边界类型(父类Disk)。
Constant pool: #1 = Methodref #4.#18 // java/lang/Object."<init>":()V #2 = Fieldref #3.#19 // Computer.disk:LDisk; #3 = Class #20 // Computer #14 = Utf8 setDisk #19 = NameAndType #5:#6 // disk:LDisk; public void setDisk(T); descriptor: (LDisk;)V flags: (0x0001) ACC_PUBLIC Code: ...
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html
ArrayList<Integer> intList = new ArrayList<>(); ArrayList rawList = new ArrayList(); System.out.println(intList.getClass().getCanonicalName()); System.out.println(rawList.getClass().getCanonicalName());
Output:
java.util.ArrayList java.util.ArrayList
ppublic class Holder<T> { private T obj; public void set(T obj){ this.obj = obj; } public T get(){ return obj; } public void testT(Object arg){ if (arg instanceof T){ ... } //编译错误 T var = new T(); //编译错误 T[] array = new T[100]; //编译错误 } } }
public class Holder<T> { private T obj; //在编译时,该类中的所有的T都会被替换为边界类型Object。 public void set(T obj){ this.obj = obj; } public T get(){ return obj; } public static void main(String[] args){ Holder<Integer> holder = new Holder<>(); //编译器会检查实参是不是一个Integer, //虽然这里的1是int类型,但是因为自动包装机制的存在, //他会被转化为一个Integer,因此能够通过类型检查。 holder.set(1); //编译器也会进行类型检查, //并且自动插入一个Object类型到Integer类型的转型操作。 Integer obj = holder.get(); } }
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #3 // class Holder 3: dup 4: invokespecial #4 // Method "<init>":()V 7: astore_1 8: aload_1 9: iconst_1 10: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 13: invokevirtual #6 // Method set:(Ljava/lang/Object;)V 16: aload_1 17: invokevirtual #7 // Method get:()Ljava/lang/Object; 20: checkcast #8 // class java/lang/Integer 23: astore_2 24: return
class Holder<T>{ private T t; public void init(IFactory<T> factory){ this.t = factory.create(); // 此处即为new T()的工厂方法的实现 } } interface IFactory<T>{ //接口也可以参数化 T create(); } class IntegerFactory implements IFactory<Integer>{ public Integer create(){ return new Integer(10); } } public class newTwithFactory{ public static void main(String[] args){ Holder<Integer> holder = new Holder<>(); holder.init(new IntegerFactory()); } }
class Holder<T>{ private T t; private Class<T> kind; public Holder(Class<T> kind){ this.kind = kind; } public void init(){ try{ this.t = kind.newInstance(); }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args) { Holder<Integer> holder = new Holder<>(Integer.class); holder.init(); } }
用extends申明对参数类型的限制条件
extends
interface HasColor{ java.awt.Color getColor(); } class Colored <T extends HasColor>{...} class Dimension { public int x,y,z; } class ColoredDimension <T extends HasColor & Dimension>{...} //错误! class ColoredDimension <T extends Dimension & HasColor>{ //why? }
class Fruit{} class Apple extends Fruit{} public class NonConvariantGeneric { List<Fruit> flist = new ArrayList<Apple>(); //编译错误 }
Apple的List不是Fruit的List。Apple的List将持有Apple和Apple的子类型,Fruit的List将持有任何类型的Fruit。是的,这包括Apple,但是它不是一个Apple的List,它仍然是Fruit的List。Apple的List在类型上不等价于Fruit的List,即使Apple是一种Fruit类型。
List
Fruit
class Fruit {} class Apple extends Fruit {} class Plate<T>{ private T item; public Plate(T t){item=t;} public void set(T t){item=t;} public T get(){return item;} } //现在我定义一个“水果盘子”,逻辑上水果盘子当然可以装苹果。 //但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。 Plate<Fruit> p = new Plate<Apple>(new Apple()); //编译错误!
协变与逆变(Covariance and contravariance )是在计算机科学中,描述具有父/子型别关系的多个型别通过型别构造器、构造出的多个复杂型别之间是否有父/子型别关系的用语。
https://zh.wikipedia.org/wiki/协变与逆变
class Fruit{} class Apple extends Fruit{} public class GenericsAndCovariance { public static void main(String[] args){ //一个能放水果以及一切是水果派生类的盘子,啥水果都能放的盘子 //Plate<? extends Fruit>和Plate<Apple>最大的区别就是: //Plate<? extends Fruit>是Plate<Fruit>以及Plate<Apple>的基类。 Plate<? extends Fruit> p=new Plate<Apple>(new Apple()); // a list of any type that's inherited from Fruit List<? extends Fruit> flist = new ArrayList<Apple>(); } }
class Food{} //Lev 2 class Fruit extends Food{} class Meat extends Food{} //Lev 3 class Apple extends Fruit{} class Banana extends Fruit{} class Pork extends Meat{} class Beef extends Meat{} //Lev 4 class RedApple extends Apple{} class GreenApple extends Apple{}
https://www.zhihu.com/question/20400700
class Fruit{} class Apple extends Fruit{} Plate<? extends Fruit> p=new Plate<Apple>(new Apple()); //不能存入任何元素 p.set(new Fruit()); //Error p.set(new Apple()); //Error //读取出来的东西只能存放在Fruit或它的基类里。 Fruit newFruit1=p.get(); Object newFruit2=p.get(); Apple newFruit3=p.get(); //Error
表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子。
Plate<?super Fruit>覆盖图中红色的区域。
Plate<?super Fruit>
class Fruit{} class Apple extends Fruit{} public class GenericsAndCovariance { public static void main(String[] args){ Plate<? super Fruit> p=new Plate<Fruit>(new Fruit()); //存入元素正常 p.set(new Fruit()); p.set(new Apple()); //读取出来的东西只能存放在Object类里。 Apple newFruit3=p.get(); //Error Fruit newFruit1=p.get(); //Error Object newFruit2=p.get(); } }
///无界通配符 Unbounded Wildcards List<?>
List<Object>
public class UseList<W,T>{ void f(List<T> v){} void f(List<W> v){} }
class SelfBounded<T extends SelfBounded<T>>{ ... }
https://stackoverflow.com/questions/19588449/self-bounded-generics
https://jishu.dev/2021/03/03/java-self-bounded-types/
public class BasicHolder<T> { T element; void set(T arg){this.element = arg;} T get(){return this.element;} void print(){ System.out.println(element.getClass().getSimpleName()); } } public class SubType extends BasicHolder<SubType> { public static void main(String[] args){ SubType s1 = new SubType(); SubType s3 = s1.get(); s1.print(); s3.print(); } }
public class SubType extends BasicHolder<SubType> {} public class Plate extends BasicHolder<Plate>{} ...
... the generic base class becomes a kind of template for comon functionality for all its derived class
... but this functionality will use the derived type for all of its arguments and return values ...
class Other{} class BasicOther extends BasicHolder<Other>{} public static void main(String[] args){ BasicOther b = new BasicOther(), b2 = new BasicOther(); b.set(new Other()); Other other = b.get(); b.print();// Other }
//forcing the generic to be used as its own bound argument 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> {} //ok class D; class E extends SelfBounded<D>{} //error public static void main(String[] args){ A a = new A(); a.set(new A()); a.print(); B b = new B(), a2 = new B(); //b.set(b2); //Error //b.print(); }