Java泛型

泛型

泛型,Generic。允许在定义类,接口,方法时使用类型形参。
这个类型形参在声明变量,创建对象,调用方法时动态指定,传入类型实参。

集合

集合框架中接口和类增加了泛型支持。
Java集合对元素类型没有限制,在编译时不检查类型。 对象加入后,取出会变成 Object 类型, 需要强制转换。 如果类型不一致,会导致 ClassCastException 异常。
Java参数化类型(parameterized type)允许创建时指定元素的类型。
在编译时如果类型不对会报错。

        List<String> strList = new ArrayList<String>();
        strList.add("str");

        //编译报错
        strList.add(5);
        strList.forEach(str-> System.out.println((String) str));

List定义

public interface List<E> extends Collection<E> {
	Iterator<E> iterator();
	
	boolean add(E e);
}

菱形语法

        List<String> strList = new ArrayList<String>();
        Map<String, Integer> strMap = new HashMap<String, Integer>();
        

Java 7开始允许在构造器后面不需要带完整的泛型信息,只要一对尖括号(<>) 即可,Java可以推断出后面信息。

        List<String> strList = new ArrayList<>();
        Map<String, Integer> strMap = new HashMap<>();

泛型接口、类

任何接口、类可以定义泛型声明。

class G<T>{
    private T info;

    public G(){}

    public G(T info){
        this.info = info;
    }

    public T getInfo() {
        return info;
    }

    public void setInfo(T info) {
        this.info = info;
    }
}
public class GenericTest {
    public static void main(String[] args){
        G<String> a1 = new G<>("str");
        System.out.println(a1.getInfo());

        G<Integer> a2 = new G<>(2);
        System.out.println(a2.getInfo());
   }
}
//Output
str
2


上面声明了一个 G<T> 类, 实际调用时为 T指定实际类型。
构造器为 G, 而不是 G<T>。

派生子类

当创建了带泛型声明的接口或父类时,可以为接口创建实现类,或派生子类。
这时父类不能包含类型形参。下面写法是错误的。

 class A extends G<T>{
 }

使用泛型必须指定实际类型。
下面是两种定义方式:

class GA extends G<String>{
    @Override
    public String getInfo() {
        return super.getInfo();
    }

    @Override
    public void setInfo(String info) {
        super.setInfo(info);
    }
}
class GB extends G{
    @Override
    public String getInfo() {
        return super.getInfo().toString();
    }

    @Override
    public void setInfo(Object info) {
        super.setInfo(info);
    }
}

第一个G<String>, 子类都会继承String getInfo() 。
第二个G, 子类会把 T形参都当做 Object来使用。

静态变量

不管泛型的实际类型参数是什么,运行时都当做同一个类来处理。
在内存中只占用一块内存空间,因此在静态方法, 静态变量,静态初始化块中不允许使用类型形参。

类型通配符

数组和泛型不同, 如果Foo是Bar的子类型, 那么Foo[] 是 Bar[]的子类型, G<Foo> 不是 G<Bar> 的子类型。
为了表示泛型的父类, 可以使用类型通配符, (?)称为通配符。
List<?> 表示各种泛型List的父类, 并不能把元素加入。

    public static void test(List<?> objects){
        objects.forEach(o -> System.out.println(o));
    }
    //调用test
    List<String> strList = new ArrayList<>();
    strList.add("zhangsan");
    test(strList);

类型通配符上限

使用 List<? extends Number> 这种是受限通配符的例子,这个Number是通配符的上限。

    public static void test(List<? extends Number> objects){
        objects.forEach(o -> System.out.println(o));
    }
    //使用
        List<Integer> intList = new ArrayList<>();
        intList.add(5);
        test(intList);
        List<String> strList = new ArrayList<>();
        strList.add("zhangsan");
        test(strList);  //报错

Interger是Number的子类, String不是, List<String>会编译报错。

类型形参上限

定义类型形参是设置上限,用于表示实际类型要么是该上限类型,要么是上限类型的子类。

class G<T extends java.lang.Number>{
    private T info;


    public G(){}

    public G(T info){
        this.info = info;
    }

    public T getInfo() {
        return info;
    }

    public void setInfo(T info) {
        this.info = info;
    }
}
public class GenericTest {
    public static void main(String[] args){
        G<Integer> a2 = new G<>(2);
        System.out.println(a2.getInfo());
        //编译报错
        G<String> a1 = new G<>("str");
        System.out.println(a1.getInfo());
}

可以设置多个上限,至多一个父类上限,可以有多个接口上限。定义时接口上限必须在类上限后面。

class G<T extends java.lang.Number & Serializable>{
    private T info;
}

泛型方法

泛型方法 Generic Method。在声明方法时定义一个或多个类型形参。

    static <T> void arrayToCollection(T[] a, Collection<T> c){
        for(T o: a){
            c.add(o);
        }
    }
    //调用
    Object[] oa  = new Object[100];
    Collection<Object> co = new ArrayList<>();
    arrayToCollection(oa, co);

    String[] sa = new String[10];
    Collection<String> cs = new ArrayList<>();
    arrayToCollection(sa, cs);
    

声明方法时,多了类型形参声明。
方法中的泛型参数无须显式传入实际类型参数,编译器会根据实参推断出类型形参的值。

泛型方法和通配符

大多数都可以使用泛型方法来代替类型通配符。

public interface Collection<E> extends Iterable<E> {
   boolean containsAll(Collection<?> c);
   boolean addAll(Collection<? extends E> c);
}

可以改为泛型方法

   <T> boolean containsAll(Collection<T> c);
   <T extends E> boolean addAll(Collection<T> c);

泛型构造器

泛型构造器是在构造器签名中声明类型形参。

class Foo{
    public <T> Foo(T t){
        System.out.println(t);
    }
}
public class GenericTest {
    public static void main(String[] args){
        new Foo("str");
        new Foo(200);
        //显式指定泛型构造器T参数
        new <String> Foo("str 2");
        //类型不一致,报错
        new <String> Foo(1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值