泛型

Java集合有个缺点,把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)。Java集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。但这样做带来如下两个问题:
1.集合对元素类型没有任何限制,这样可能引发一些问题。例如,想创建一个只能保存Dog对象的集合,但程序也可以轻易地将Cat对象“丢”进去,所以可能引发异常。
2.由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是Object, 因此取出集合元素后通常还需要进行强制类型转换。这种强制类型转换既增加了编程的复杂度,也可能引发ClassCastException异常。
泛型就是来解决这类问题的。

泛型入门

参数化类型(泛型)允许在创建集合时指定集合的元素类型。
泛型语法例子:List strList = new ArrayList<>() ;
Map<string,Integer> scores = new HashMap<>() ;
分别为指定List集合类型为字符串类型,Map集合类型为字符串类型,映射值为整形。

深入泛型

所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。可以在声明集合变量、创建集合对象时传入类型实参。
只定义一个List接口,但实际使用时可以产生无数多个List 接口,只要为E传入不同的类型实参,系统就会多出一个新的List 子接口。
也就是我可以随意定义一个List< E >接口,他的返回值会是List< E >,是一种特殊的数据类型,可以看成List的子类。
List< String >绝不 会被替换成ListString,系统没有进行源代码复制,二进制代码中没有,磁盘中没有,内存中也没有。
定义一个Apple< T >类型,我们可以给T类型形参传入各种实际类型。
但是如果我们给它创建了子类(子接口)那么子类(子接口)里就不能带有类型形参。例如:public class A extends Apple< string >(里面就不能是类型形参T了)它的子类将会继承到String getlInfo0和voidsetInfo(String info)两个方法,如果子类需要重父类的方法,就必须注意这一点。
用了泛型后原来的类或接口本身不会受到影响,还是他原来的性质,使用泛型后他不会变成一个新的类或新的接口,泛型只是给他加了一点限制条件。
不管为泛型的类型形参传入哪一种类型实参, 对于Java来说,它们依然被当成同一个类处理,在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。
由于系统中并不会真正生成泛型类,所以instanceof 运算符后不能使用泛型类。
List< String >对象不能被当成List< Object >对象,因此泛型中Object并不是哪都能用的。泛型也不允许不同类型之间赋值

类型通配符

使用类型通配符(?),可以表示各种泛型的父类。但是仅在这种情况下还不能把元素(null除外)加进去,因为类型还不明。另一方面,程序可以调用get)方法来返回List<?>集合指定索引处的元素,其返回值是一个未知类型,但可以肯定的是,它总是一个Object。因此,把get()的返回值赋值给一个 Object类型的变量,或者放在任何希望是Object 类型的地方都可以。
List<? extends Shape >可以用来表示仅为Shape类型,也就是只能有Shape的子类传进集合。如果只是List< Shape >是不可行的,因为泛型中不会认为这是谁的父类,而上面那种形式相当于加了说明Shape为父类。
但是同时,他现在还是一个父类,不能确定集合要存放的类型,还是无法直接把元素(null除外)放进集合。

Java泛型不仅允许在使用通配符形参时设定上限,而且可以在定义类型形参时设定上限,用于表示传给该类型形参的实际类型要么是该上限类型,要么是该上限类型的子类。

泛型方法

声明一个泛型方法的例子:static < T > void fromArrayToCollection(T[] a, Collection< T > c)
在这里插入图片描述
上面程序定义了一个泛型方法,该泛型方法中定义了一个T类型形参,这个T类型形参就可以在该方法内当成普通类型使用。与接口、类声明中定义的类型形参不同的是,方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。与类、接口中使用泛型参数不同的是,方法中的泛型参数无须显式传入实际类型参数,如上面程序所示,当程序调用fromArrayToCollection(方法时,无须在调用该方法前传入String、Object 等类型,但系统依然可以知道类型形参的数据类型,因为编译器根据实参推断类型形参的值,它通常推断出最直接的类型参数。

类型通配符与泛型方法(在方法签名中显式声明类型形参)有一个显著的区别:类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的类型形参必须在对应方法中显式声明。

一旦定义了泛型构造器,接下来在调用构造器时,就不仅可以让Java根据数据参数的类型来“推断”类型形参的类型,而且程序员也可以显式地为构造器中的类型形参指定实际的类型。如果程序显式指定了泛型构造器中声明的类型形参的实际类型,则不可以使用“菱形”语法。
设定通配符下限可以防止copy()这种方法丢失数据类型。
语法:<? superI type>
TreeSet有一个构造器也用到了这种设定通配符下限的语法,
TreeSet (Comparator<? super E>C)
如果泛型方法里面有方法重载可能会出现调用时两种方法都符合条件会导致编译错误。
Java8改进了泛型方法的类型推断能力,类型推断主要有如下两方面:
1.可通过调用方法的上下文来推断类型参数的目标类型。
2.可在方法调用链中,将推断得到的类型参数传递到最后一个方法。

摩擦和转换

当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉。比如一一个List< String > 类型被转换为List,则该List 对集合元素的类型检查变成了类型参数的上限(即Object)。这种为摩擦
把非泛型对象赋给泛型对象为转换(运行异常)

泛型与数组

数组元素类型不能包括类型变量或类型形参,除非是无上限的类型通配符,但可以声明元素类型包含类型变量或类型形参的数组。也就是说,只能声明List< String >[]形 式的数组,但不能创建ArrayList< String >[10]这样的数组对象。

看完泛型了,但是感觉看完了还是比较乱,不大会用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值