泛型

在定义时不指定具体的类型,在使用时才指定具体的类型。

一.泛型类

1-1基本使用

在类上使用类型参数时,用尖括号括住,放在类名后面,如果有多个类型参数,则使用逗号隔开。然后在使用这个类时,再用实际的类型替换此类型参数。

package com.hutao.page.chapter15.page354;

class Automobile {
}

public class Holder3<T> {
    private T a;

    public Holder3(T a) {
        this.a = a;
    }

    public void setA(T a) {
        this.a = a;
    }

    public T getA() {
        return a;
    }

    public static void main(String[] args) {
        Holder3<Automobile> automobileHolder = new Holder3<Automobile>(new Automobile());
        Automobile a = automobileHolder.getA();
        //automobileHolder.setA("Not a automobile");//Error
        //automobileHolder.setA(1);//Error
    }
}

1-2继承泛型类

在继承泛型类时需注意类型参数的指定。

举例说明:

1.TwoTuple类具有两个类型参数A,B:

package com.hutao.page.chapter15.page355;

public class TwoTuple<A, B> {
    public final A first;
    public final B second;

    public TwoTuple(A first, B second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ")";
    }
}

2.继承泛型类TwoTuple

①在继承时不指定泛型父类的类型参数

package com.hutao.page.chapter15.page355;

public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {
    public C third;

    public ThreeTuple(A first, B second, C third) {
        super(first, second);
        this.third = third;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ", " + third + ")";
    }
}

②在继承时指定泛型父类的全部类型参数

package com.hutao.page.chapter15.page355;

public class ThreeTuple1<C> extends TwoTuple<Integer, String> {
    public C third;

    public ThreeTuple1(Integer first, String second, C third) {
        super(first, second);
        this.third = third;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ", " + third + ")";
    }
}

③在继承时指定泛型父类的部分类型参数

package com.hutao.page.chapter15.page355;

public class ThreeTuple2<B, C> extends TwoTuple<Integer, B>{
    public C third;

    public ThreeTuple2(Integer first, B second, C third) {
        super(first, second);
        this.third = third;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ", " + third + ")";
    }
}

测试:

package com.hutao.page.chapter15.page355;

class Bike {
}

class Moto {
}

class Car {
}

public class Test {
    public static void main(String[] args) {
        //在继承时不指定泛型父类的类型参数的ThreeTuple<A,B,C>
        ThreeTuple<Bike, Moto, Car> threeTuple = new ThreeTuple<Bike, Moto, Car>(new Bike(), new Moto(), new Car());
        System.out.println(threeTuple);

        //在继承时指定泛型父类的全部类型参数的ThreeTuple1<C>
        ThreeTuple1<Car> threeTuple1 = new ThreeTuple1<Car>(1,"en",new Car());
        System.out.println(threeTuple1);

        //在继承时指定泛型父类的部分类型参数的ThreeTuple2<B, C>
        ThreeTuple2<Moto, Car> threeTuple2 = new ThreeTuple2<Moto, Car>(2,new Moto(), new Car());
        System.out.println(threeTuple2);
    }
}

运行结果为:

(com.hutao.page.chapter15.page355.Bike@4554617c, com.hutao.page.chapter15.page355.Moto@74a14482, com.hutao.page.chapter15.page355.Car@1540e19d)
(1, en, com.hutao.page.chapter15.page355.Car@677327b6)
(2, com.hutao.page.chapter15.page355.Moto@14ae5a5, com.hutao.page.chapter15.page355.Car@7f31245a)

Process finished with exit code 0

总结:

  1. 在继承泛型类时,可在extends关键字后指定泛型父类的全部或者部分类型参数;如:
  2. 如果没有全部指定,则需在子类的泛型参数列表(<>中)添加未指定的类型参数,以确保在使用时,父类的所有类型参数都有有效的值。

1-3其他

  1. 普通内部类可以访问其外围类的类型参数,但静态内部类不可以,因为静态内部类不能直接访问其外围类的非静态成员;
  2. 泛型接口和泛型类的使用一致。

 

二.泛型方法

  1. 方法能否为泛型方法与其所在类是否是泛型类没有关系;
  2. 对于一个static方法而言,无法访问泛型类的类型参数,所以,如果static方法需要使用泛型能力,就必须使其成为泛型方法。

2-1基本使用

要定义泛型方法,只需将泛型参数列表置于方法返回值之前。

package com.hutao.page.chapter15.page361;

public class GenericMethods {
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }

    public static void main(String[] args) {
        GenericMethods genericMethods = new GenericMethods();
        genericMethods.f("");
        genericMethods.f(1);
        genericMethods.f(1.0);
        genericMethods.f(1.0f);
        genericMethods.f('c');
        genericMethods.f(genericMethods);
    }
}

运行结果为:

java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
com.hutao.page.chapter15.page361.GenericMethods

Process finished with exit code 0

2-2类型参数推断

在使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明类型参数的值,因为编译器会自动找出具体的类型。这称为类型参数推断

如,在2-1中调用public <T> void f(T x)方法时,并未显式指定类型参数。

泛型方法可通过①传入的实参②接受方法返回值的引用来进行类型参数推断。

1.根据传入的实参进行类型参数推断

实参和形参是对应的,如果形参是泛型,那么可通过实参的类型推断出类型参数。

package com.hutao.page.chapter15.page361;

public class GenericMethods {
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }

    public static void main(String[] args) {
        GenericMethods genericMethods = new GenericMethods();
        genericMethods.f(""); //形参为(T x),传入的实参类型为String,所以推断出类型参数T为String
        genericMethods.f(1);  //形参为(T x),传入的实参类型为Integer,所以推断出类型参数T为Integer
        genericMethods.f(1.0);
        genericMethods.f(1.0f);
        genericMethods.f('c');
        genericMethods.f(genericMethods);
    }
}

2.根据接受方法返回值的引用进行类型参数推断

方法的返回值和接受方法返回值的引用是对应的,如果方法的返回值是有泛型的,那么可以通过接受方法返回值的引用的具体类型推断出类型参数。

package com.hutao.page.chapter15.page362;

import java.util.*;

public class New {
    public static <K, V> Map<K, V> map() {
        return new HashMap<K, V>();
    }

    public static <T> List<T> list() {
        return new ArrayList<T>();
    }

    public static <T> LinkedList<T> linkedList() {
        return new LinkedList<T>();
    }

    public static <T> Set<T> set() {
        return new HashSet<T>();
    }

    public static <T> Queue<T> queue() {
        return new LinkedList<T>();
    }

    public static void main(String[] args) {
        Map<String, List<String>> map = New.map();
        List<String> list = New.list();
        LinkedList<String> linkedList = New.linkedList();
        Set<String> set = New.set();
        Queue<String> queue = New.queue();
    }
}

拿List<String> list = New.list();来说,list方法定义的返回值是List<T>,而接受list方法返回值的引用为List<String>,所以推断出类型参数T为String。

但是,这种根据接受方法返回值的引用进行类型参数推断的方式只适用于普通的赋值语句(即需用赋值运算符=连接的语句)。如果将一个泛型方法的调用结果(例如New.map())作为参数传递给另一个方法,这时编译器不会进行类型参数推断。

2-3显示指定泛型方法的类型参数

  1. 在点操作符与方法名之间插入尖括号,然后把类型置于尖括号内;
  2. 如果是在定义该方法的类的内部,必须在点操作符前使用this关键字(不然点操作符前没东西);
  3. 如果使用的是静态方法,必须在点操作符之前加上类名。

2-2 2中的问题(非赋值语句不能根据接受方法返回值的引用进行类型参数推断),就可使用这种显示指定类型参数的方法来解决。

import com.hutao.page.chapter15.page362.New;

import java.util.List;

public class LimitsOfInference {
    static void f(List<String> list){
    }

    public static void main(String[] args) {
        f(New.<String>list());
    }
}

2-4根据类型参数创建对象

一个泛型方法,可推断出类型参数T的具体值,并根据推断出的类型参数创建该类型的对象。

不能使用new关键字直接创建类型参数对应类的对象(Type parameter 'T' cannot be instantiated directly.):

可为方法传入要创建对象的Class类型实参,通过2-2.1中的方式推断出类型参数,然后通过反射创建新的对象。

package com.hutao.test.chapter15.page364;

class Dog {
}

public class Test {
    public static <T> T createObject(Class<T> type) {
        try {
            return type.newInstance();
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        Dog dog = createObject(Dog.class);
        System.out.println(dog);
    }
}

运行结果为:

com.hutao.test.chapter15.page364.Dog@4554617c

Process finished with exit code 0

 

三.擦除

在泛型代码内部,无法获取任何有关泛型参数类型的信息。即,如果是泛型类,则无法在类内部获取类型参数的具体值;如果是泛型方法,则无法在方法内部获取类型参数的具体值

 

posted @ 2022-11-22 10:24  certainTao  阅读(52)  评论(0编辑  收藏  举报