泛型:Generic Programming
- 泛型类:ArrayList,HashSet,HashMap等
- 泛型方法:Collections.binarySearch,Arrays.sort 等
- 泛型接口:Iist,Iterator 等
- 泛型的本质:参数化类型,避免类型转换,代码可复用
同类
C++的模板(Template)
C#的泛型
//Collections.binarySearch 方法支持泛型
int pos1 = Collections.binarySearch(list, "456");
int pos2 = Collections.binarySearch(list2, "456");
//Iterator 接口支持泛型
Iterator<String> iter = list.iterator();
while (iter.hasNext()){
System.out.println(iter.next());
}
Iterator<String> iter2 = set1.iterator();
while (iter2.hasNext()){
System.out.println(iter2.next());
}
自定义泛型
泛型类
具有泛型变量的类
在类名后用<T>代表引入类型
- 多个字母表示多个引入类型如<T,U>等
- 引入类型可以修饰成员变量/局部变量/参数/返回值
- 没有专门的template关键字
泛型类调用
传入具体的类
Interval<Integer>v1 = new Interval<Integer>(1,2);
Interval<Integer>v1 = new Interval<>(1,2);
package org.example.defaultmethod;
public class Interval<T> {
private T lower;
private T upper;
public Interval(T lower, T upper) {
this.lower = lower;
this.upper = upper;
}
public T getLower() {
return lower;
}
public T getUpper() {
return upper;
}
}
package org.example.defaultmethod;
public class FTest {
public static void main(String[] args){
Interval<Integer> v1 = new Interval<Integer>(1,2);
int lower = v1.getLower();
int upper = v1.getUpper();
System.out.println(lower+","+upper);
Interval<Integer> v2 = new Interval<>(1,2);
Interval<Integer> v3 = getRverse(v2);
System.out.println(v3.getLower()+","+v3.getUpper());
}
// <T> 泛型方法指定的类型 Interval<T> 返回值
public static <T> Interval<T> getRverse(Interval<T> interval) {
return new Interval<T>(interval.getUpper(), interval.getLower());
}
}
泛型方法
- 具有泛型参数的方法
- 该方法可在普通类/泛型类中
- <T>在修饰符后,返回类型前
package org.example.defaultmethod;
public class ArrayUtil {
public static <T> T getMiddle(T... a)
{
return a[a.length/2];
}
}
package org.example.defaultmethod;
public class FFTest {
public static void main(String[] args){
String s1 = ArrayUtil.<String>getMiddle("abc","def","ghi");
Integer i1 = ArrayUtil.getMiddle(1,2,3);//可以推断出来
//null is ok
String s2= ArrayUtil.<String>getMiddle("abc","def",null);
//error 寻找共同超类 再转型
//Integer i1 = ArrayUtil.getMiddle(1,2.5f,3L);
}
}
泛型接口
- 和泛型类相似,在类名后加<T>
- T用来指定方法返回值和参数
- 实现接口时,指定类型
package org.example.defaultmethod;
public interface Calculator<T> {
public T add(T operand1, T operand2);
}
package org.example.defaultmethod;
public class IntegerCalculator implements Calculator<Integer>{
public Integer add(Integer operand1, Integer operand2){
return operand1+operand2;
}
}
package org.example.defaultmethod;
public class ICTest {
public static void main(String[] args){
IntegerCalculator c1 = new IntegerCalculator();
System.out.println(c1.add(1,2));
Calculator<Integer> c2 = new IntegerCalculator();
System.out.println(c2.add(1,2));
}
}
泛型接口
T也可以再是一个泛型类
package org.example.defaultmethod;
public class IntervalCalculator implements Calculator<Interval<Integer>>{
public static void main(String[] args){
Calculator<Interval<Integer>> c = new IntervalCalculator();
Interval<Integer> operand1 = new Interval<Integer>(1,2);
Interval<Integer> operand2 = new Interval<Integer>(3,4);
Interval<Integer> operand3 = c.add(operand1, operand2);
System.out.println("{"+operand3.getLower()+","+operand3.getUpper()+"}");
}
public Interval<Integer> add(Interval<Integer> operand1, Interval<Integer> operand2){
int lower = operand1.getLower()+operand2.getLower();
int upper = operand1.getUpper()+operand2.getUpper();
return new Interval<Integer>(lower,upper);
}
}
泛型限定
编写的代码可以被很多不同类型的对象所重用
特定场合下,需要对类型进行限定(使用某些特定方法)
- <T extends Comparable>约定T必须是Comparable的子类如<'T extendsextends固定,后面可以多个,以&拼接Comparable & Serializable>
- cxtends限定可以有多个接口,但只能一个类,且类必须排第一位逗号隔参数,<T extends File & Cloneable,U extends Serializable>
泛型类之间的继承
Pair<S>和Pair<T>没有任何关系,无论S和T之间是什么关系
Pair<Apple> apples = new Pair<>(new Apple(3), new Apple(4));
Pair<Fruit> fruits = apples;
fruits.setFirst(new orange(5));
Apple泛型类可以扩展或实现其他的类,如如ArrayList<T>实现List<T>
List<Orange>oranges = new ArrayList<orange>();
上限界定符,Pair<?extends S>Pair能接收的参数类型,是S自身或子类
Pair<?extends Fruit>代表Pair<Fruit>,Pair<Apple>,Pair<Orange>等
//Pair<Apple>and Pair<orange>皆可
//Fruit的子类,都有getsize()方法
public void printFruits(Pair<? extends Fruit> fruits) {
Fruit first=fruits.getFirst()
Fruit second=fruits.getsecond();
System.out.println("The sizes of fruits are+ first.getsize()++ second.getsize());
}
只能get/不能set,编译器只能保证出来的类型,但不保证放入的对象是什么类型
- Pair<? extends Fruit>代表Pair<Fruit>, Pair<Apple>,Pair<Orange>等
- ?extends Fruit getFirst0;//肯定可以转型到Fruit·
- void setFirst(?extends Fruit)//未知具体的类型,错误
Pair<Apple> apples = new Pair<>(new Apple(3),new Apple(4));Pair<? extends Fruit> fruits = apples;fruits.setFirst(new orange(5));//编译错误
下限界定符,Pair<? super S>
Pair能接收的类型参数,是S的自身或超类
。Pair<? super Apple> 代表Pair<Object>, Pair<Fruit>,Pair<Apple>等
Pair<? super Apple> fruits = new Pair<Fruit>();
fruits.setFirst(new Apple(5));//Apple到超类的转型
fruits.setsecond(new GreenApple(5));//GreenApple到超类的转型
fruits.setsecond(new object());//无法转型为Apple的超类
只能set/不能get,编译器保证放入的是S本身或超类,但不保证出来是什么具体类型
Pair<? super Apple>代表Pair<Object>, Pair<Fruit>,Pair<Apple>等
void setFirst(? super Apple)//可以放入Apple及子类对象
?super Apple getFirst0 //无法得知出来的对象类型,只能是Object
Pair<? super Apple> fruits = new Pair<Fruit>();
fruits.setFirst(new Apple(5));//Apple到超类的转型
fruits,setsecond(new GreenApple(5));//GreenApple到超类的转型
Fruit obj= fruits,getFirst();//也未知其超类对象的方法,故报错
fruits.getFirst()).hello();//也未知其超类对象的方法,故报错
泛型PECS原则
- Producer Extends, Consumer Super
- 要从泛型类读取类型T的数据,并且不能写入,可以使用?extends 通配符;roducer Extends,泛型类是生产者,往外输出东西)
- 如果要向泛型类写入类型T的数据,并且不需要读取,可以使用?super 通配符;(ConsumerSuper,泛型类是消费者,往内增加东西)
- 如果既想写入又想读出,那就不用通配符
无限定类型的泛型
Pair<T>,原始类型
Pair<?>,无限定通配符,表示任意类型
·如Pair<Object>, Pair<apple>,Pair<Orange·?getFirst0 //不确定出来是什么类型,只能赋值给Objectvoid setFirst0 //无法放入任何对象,甚至是Object
JVM里面没有泛型对象,而是采用类型檫除技术 只有普通的类和方法
檫除泛型变量 替换为原始类型raw type,无限定为Object
public class Pair<T>{
private Tfirst;
private Tsecond;
public Pair(T first,T second){this,first = first;this.second=second;}
public T getFirst(){return first;}
public void setFirst(T first){this.first = first;}
}
public class Pair {
private object first;
private object second;
public Pair(object first, object second){this.first = first;this.second = second;}
public object getFirst(){return first;}
public void setFirst(object first){this.first = first;}
}
有限定位第一个类型
public class NewPair<T extends comparable & serializable>
private T first;
private T second;
public NewPair(T first, T second){this,first = first;this.second = second;}
public T getFirst(){return first;}
public void setFirst(T first){this.first = first;}
}
public class NewPair {
private comparable first;private comparable second;
public NewPair(comparable first, comparable second){this,first = first;this.second = second;}
public comparable getFirst(){return first;}
public void setFirst(comparable first){this.first = first;}
}
擦除泛型变量后,为了保证类型的安全性,需要自动进行类型转换
泛型表达式翻译
-Fruit a = fruits.getFirst(;
Object a1 = fruits.getFirst(;
Fruit a = (Fruit) a1;
重载泛型方法翻译(自动桥方法)
泛型:类型擦除
- 虚拟机中没有泛型,只有普通类和方法-在编译阶段,
- 泛型参数被擦除为限定类型,并进行相关类型转换
- 虚拟机也会合成桥方法来保持方法多态
JAVA 类型的协变和逆变
Type Variance形式化定义:
- A、B是类型,f(·)表示类型转换,<表示继承关系:如A≤B,表示A继承于B
- f(·)是协变(covariant)的,如果A≤B,有f(A)<f(B)
- f(·)是逆变(contravariant)的,如果A≤B,有f(B)<f(A)
- f(·)是不变(invariant)的,当上述两种都不成立,即f(A)和f(B)没有关系
- f(·)是双变(bivariant)的,如果A≤B,有f(A)<f(B) 和f(B)<f(A)同时成立
Java数组是协变的
String 是Object的子类,String[] 是Object[] 的子类
class A{} //第一代
class B extends A{} //第二代
class c extends B{} //第三代
B[] array1 =new B1];
array1l0l=new B;
A[]array2 = array1;
try {
array2[0]=new A();
//compile ok, runtime error 父类无法转化为子类
}catch(Exceptionex){
ex.printstackTrace();
}
try {
array2[o]=new C();
//compile ok, runtime ek 子类可以转化为父类
}catch(Exceptionex){
ex.printstackTrace();
}
Java的(原始的)泛型是不变的
String是Object的子类,Iist<String>和Iist<Object>没有关系
class A{} //第一代
class B extends A{} //第二代
class c extends B{} //第三代
ArrayList<B> list1 = new ArrayList<B>();
list1.add(new B());
ArrayList<A> list2 = list1;
//complile error
泛型可采用通配符,支持协变和逆变(PECS原则)
- ArrayList<? extends A> list3=new ArrayList<B>0;//协变
- ArrayList<? super B> list4 = new ArrayList<A>O;//逆变
复合情况
数组协变,泛型不变
class A{} //第一代
class B extends A{} //第二代
class c extends B{} //第三代
public static void testArrayAndList()
{
B[] rl= test(new ArrayList<B>());//compile error
A[] r2 = test(new ArrayList<B>());//compile error
object[] r3=test(new ArrayList<object>());//compile error
A[] r4 = test(new ArrayList<A>());
Object[] r5=test(new ArrayList<A>());
}
public static A[] test(ArrayList<A> list)
{
return new A[1];
}
方法情况
- JDK1.4 重写的方法参数和返回值要求一样
- JDK1.5+重写的方法,参数要求一样的,返回值是协变的
class A{} //第一代
class B extends A{} //第二代
class c extends B{} //第三代
Father foo = new Son();
foo.f1(new B());
class Father
{
public B f1(B obj)
{
System.out.println("Father.f1()");
return new B();
}
}
class Son extends Father
{
public B f1(B obj)
//public cf1(B obi//返回值是C,也是对的
{
System.out.println("son.f1()");
return new c);
}
}