JAVA泛型

泛型: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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

larance

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值