泛型note

引入:例如我们想要在 List  l = new ArrayList()中存放Dog对象时,是将Dog——>Object,而要取出对象时,需要判断类型,将Object强转为Dog,在加入时不能对存入对象的类型进行数据类型的约束,是不安全的,也就是说我们存入一个Cat对象也能存进去。类型转换在数据量较大时对效率是有影响的。如果我们使用泛型<>,就可以在编译时检查我们存入的对象类型是否符合要求,且不再提示编译警告。

一、定义:用来指定数据类型的参数,也称参数化类型。在jdk5.0出现的,用于解决数据类型安全性问题。

二、作用:可以在声明时通过一个标识表示中某个属性的数据类型、或者方法的返回类型、参数类型。

编译的检查期间确定类型,在定义对象时指定具体的类型,可以通过getClass()获取运行时的具体类型。

三、泛型的声明(标识可以是任意多个,任意字母)

(1)接口: interface inter<T, K, E>{}

(2)类:class A<T, E, K, k> {}

(3)方法:修饰符 <T> 返回值 方法名(参数列表){}  

注意:形如public void run(E e){}这个不是方法泛型,而是该方法使用了类中的泛型E

interface Inter<T, E>{
    void run(T t);
    void say();
}


//继承接口时指定泛型类型,T是InterSon自己定义的泛型
interface InterSon<T> extends Inter<Double, String>{

}

interface InterSon2<T, K> extends Inter<T, K>{} //如果没有传入具体的值,将新定义的泛型传入父类

class AAA implements InterSon<String>{

    public<T, R> void see(T t, R r){}//定义一个泛型方法

    @Override
    public void run(Double aDouble) {//实现接口时,自动传入指定的泛型类型
//类AAA实现了接口InterSon,该接口继承自接口Inter,因此在AAA中也要实现Inter的方法,在        InterSon中定义时传入的类型会直接在实现的方法中指定。
    }

    @Override
    public void say() {

    }
}

实例化时将具体的对象传入,如:Iterator<Dog> i

注意:

(1)泛型只能传引用类型,不能传基本类型

(2)指定泛型的具体类型后,可以传入其类型或其子类型,如:

public static void main(String[] args) {
        Person<A, A> p = new Person<A, A>(new B());
//给泛型类型指定具体类型为A,但是可以传入其子类B的对象
        Person[] cc = new Person[10];
        System.out.println(cc);
    }

class A{}
class B extends A{}

class Person<E,K>{
    public Person(K k){

    }
}

这里指的是传入的指定后传入的对象,其类型可以是指定类型的子类,而不是指的类,如下:

泛型的传入前后要一致!!!

//泛型的传入,前后要一致,不能是继承关系
//        List<Object> list = new ArrayList<String>();   错误

(3)定义的形式:

List<Integer> list = new ArratList<Integer> ();或者List<Integer> list = new ArratList<> ();编译器会进行类型推断。如果什么都没有写(包括<>也没有),默认传入的是Object

  (4)普通成员可以使用泛型(方法、属性)

 (5)静态成员不能使用泛型,因为静态成员和类相关,类加载的时候对象还没有创建。

(6)泛型类型是在创建类型时确定的

(7)泛型类型的数组和变量不能进行初始化,因为泛型类型会在编译期间进行类型擦除,所以在类的初始化时无法获得泛型的具体类型,但是new ArrayList<>()可以,是因为默认是Object[]

(8)自定义泛型方法可以在普通类、泛型类中使用,当调用方法时,编译器会通过传入的参数确定其类型。

如:public <T, E> see(T t, E e){}   当调用时see("aaa", 10);就确定了T和E的类型了。

四、泛型的继承和通配符

(1)泛型不具备继承性,即泛型在传入具体类型时前后要一致,不能传入具有继承关系的两个类

(2)通配符:

<?> :支持任意类型

<?  extends A>:支持A类以及A的子类,规定泛型的上限

<?  super  A>:支持A类以及A的父类,规定泛型的下限

class AA{
    @Test
    public void test1(List<?> list){
        System.out.println(list.size());
    }//可以传入任意的类型
    public void test2(List<? extends AA> list){}//只能传入AA及其子类
    public void test3(List<? super AA> list){}//只能传入AA及其父类
}
class BB extends AA{}
class CC extends BB{}


public static void main(String[] args) {
        //泛型的传入,前后要一致,不能是继承关系
//        List<Object> list = new ArrayList<String>();   错误

        AA aa = new AA();
        aa.test1(new ArrayList<Object>());
        aa.test1(new ArrayList<AA>());
        aa.test1(new ArrayList<BB>());
        aa.test1(new ArrayList<CC>());

//        aa.test2(new ArrayList<Object>());  错误,只能传入AA及其子类
        aa.test2(new ArrayList<AA>());
        aa.test2(new ArrayList<BB>());
        aa.test2(new ArrayList<CC>());

        aa.test3(new ArrayList<Object>());
        aa.test3(new ArrayList<AA>());
//        aa.test3(new ArrayList<BB>());错误,只能传入AA及其父类
//        aa.test3(new ArrayList<CC>());错误,只能传入AA及其父类

    }

五、类型擦除:

在编译时进行类型擦除,先检查传入类型在编译擦除。

擦除后原始类型都为object。

泛型类中的泛型参数的实例化是在定义对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。

public class Test2<T> {    
    public static <T >T show(T one){ //这是正确的    
        return null;    
    }    
}

因为这是一个泛型方法,在泛型方法中使用的T是自己在方法中定义的 T,而不是泛型类中的T。可以使用类名调用该方法,T由传入的参数决定

 在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到 Object。

 

 /**不指定泛型的时候*/  
int i = Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型  
Number f = Test.add(1, 1.2); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number  
Object o = Test.add(1, "asd"); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object  
    
/**指定泛型的时候*/  
int a = Test.<Integer>add(1, 2); //指定了Integer,所以只能为Integer类型或者其子类  
int b = Test.<Integer>add(1, 2.2); //编译错误,指定了Integer,不能为Float  
Number c = Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float  

public static <T> T add(T x,T y){  
        return y;  
}  

 泛型的类型检查是针对我们声明的引用的,用这个引用调用泛型方法,就会对这个引用调用的方法进行类型检测,而无关于引用的具体对象

如:

ArrayList<String> list1 = new ArrayList(); //第一种 情况
ArrayList list2 = new ArrayList<String>(); //第二种 情况

第一种情况,可以实现与完全使用泛型参数一样的效果,第二种则没有效果。

        ArrayList<String> list1 = new ArrayList();  
        list1.add("1"); //编译通过  
        list1.add(1); //编译错误  
        String str1 = list1.get(0); //返回类型就是String  

        ArrayList list2 = new ArrayList<String>();  
        list2.add("1"); //编译通过  
        list2.add(1); //编译通过  
        Object object = list2.get(0); //返回类型就是Object  

        new ArrayList<String>().add("11"); //编译通过  
        new ArrayList<String>().add(22); //编译错误  

        String str2 = new ArrayList<String>().get(0); //返回类型就是String  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值