一、基本
多态中的将基类作为方法参数类型,会带来一定的性能损耗 可是有的时候,即使使用的是接口,也依然对程序的约束太强了 泛型:参数化类型 希望类或方法,能够具备最广泛的表达能力,解耦类或方法与之所用的类型之间的约束 主要是为JDK 的容器服务
1. 自定义容器
1 . 指定容器要持有的具体的类型,而且由编译器来保证类型的正确性
2 . 与其使用Object, 更喜欢暂时不指定类型,而是稍后再决定具体使用什么类型
3 . 泛型和多态不冲突,可以放入子类
4 . 告诉编译器想使用什么类型,然后编译器帮你处理一切细节
package com. erick. day02 ;
public class Demo01 {
public static void main ( String [ ] args) {
ThirdHolder < Animal > holder = new ThirdHolder < > ( new Dog ( ) ) ;
Animal animal = holder. get ( ) ;
System . out. println ( animal. getClass ( ) . getTypeName ( ) ) ;
}
}
class Animal {
}
class Dog extends Animal {
}
class ThirdHolder < T > {
private T t;
public ThirdHolder ( T t) {
this . t = t;
}
public T get ( ) {
return t;
}
}
class SecondHolder {
private Object obj;
public SecondHolder ( Object obj) {
this . obj = obj;
}
public Object get ( ) {
return obj;
}
}
class FirstHolder {
private Integer obj;
public FirstHolder ( Integer obj) {
this . obj = obj;
}
public Integer get ( ) {
return obj;
}
}
1. 自定义元组
仅一次方法调用,就能返回多个对象,但是return语句只允许返回单个对象 创建一个对象,用它来持有想要返回的多个对象,但不想这个类包含太多的指定的类 用一组元组可以解决这个问题
1 . 该容器对象允许读取其中元素,但是不允许向其中存放新的对象( 数据传送对象/信使)
2 . 元组可以具有任意长度,元组中的对象可以是任意类型
package com. erick. day02 ;
public class Demo02 {
public static void main ( String [ ] args) {
TwoTuple < Integer , Double > result = new TwoTuple < > ( 1 , 2.3 ) ;
System . out. println ( result) ;
}
}
class FiveTuple < A , B , C , D , E > extends TwoTuple < A , B > {
public final C c;
public final D d;
public final E e;
public FiveTuple ( A a, B b, C c, D d, E e) {
super ( a, b) ;
this . c = c;
this . d = d;
this . e = e;
}
}
class TwoTuple < A , B > {
public final A a;
public final B b;
public TwoTuple ( A first, B b) {
this . a = first;
this . b = b;
}
@Override
public String toString ( ) {
return "TwoTuple{" +
"first=" + a +
", second=" + b +
'}' ;
}
}
二、泛型接口
package com. erick. day02 ;
public class Demo03 {
public static void main ( String [ ] args) {
Generator < Integer > generator = new Generator < Integer > ( ) {
@Override
public Integer next ( ) {
return 1 ;
}
} ;
System . out. println ( generator. next ( ) ) ;
}
}
interface Generator < T > {
T next ( ) ;
}
三、泛型方法
1. 基本使用
是否拥有泛型方法,与其所在的类是否是泛型没有关系 尽量使用泛型方法,而不是泛型类 泛型方法使用时,不需要指定传入的参数的类型,编译器会进行类型推断
package com. erick. day02 ;
public class Demo04 {
public static void main ( String [ ] args) {
Fruit . deleteOneFruit ( Double . valueOf ( 2.3 ) ) ;
Fruit . deleteOneFruit ( 2.3 ) ;
}
}
class Fruit {
public < T > void createOneFruit ( T t) {
System . out. println ( "creating one fruit: " + t. getClass ( ) . getSimpleName ( ) ) ;
}
public static < T > void deleteOneFruit ( T t) {
System . out. println ( "delete one fruit: " + t. getClass ( ) . getSimpleName ( ) ) ;
}
}
2. 可变参数
package com. erick. day02 ;
import java. util. ArrayList ;
public class Demo05 {
public static void main ( String [ ] args) {
get ( 1 , 2.3 , new ArrayList < String > ( ) ) ;
}
private static < T > void get ( T . . . args) {
System . out. println ( args. length) ;
}
}
四、泛型擦除
1. 泛型擦除
在使用泛型时,任何具体类型信息都被擦除,所以不同类型参数的泛型,实际上是同一种类型 比如List<String> 和 List<Integer>实际上都会被擦除为List 同一个泛型类,但是其中的具体的泛型类型不同,但是字节码class对象是相同的 在泛型代码内部,无法获得任何有关泛型参数类型的信息
package com. erick. day02 ;
import java. lang. reflect. TypeVariable ;
import java. util. ArrayList ;
import java. util. List ;
public class Demo06 {
public static void main ( String [ ] args) {
method02 ( ) ;
}
private static void method01 ( ) {
Class < ? extends ArrayList > first = new ArrayList < String > ( ) . getClass ( ) ;
Class < ? extends ArrayList > second = new ArrayList < Integer > ( ) . getClass ( ) ;
System . out. println ( first) ;
System . out. println ( second) ;
}
private static void method02 ( ) {
List < String > list = new ArrayList < > ( ) ;
TypeVariable < ? extends Class < ? extends List > > [ ] typeParameters = list. getClass ( ) . getTypeParameters ( ) ;
for ( TypeVariable type: typeParameters) {
System . out. println ( type. getName ( ) ) ;
}
}
}
2. 边界问题-擦除到父类
2.1 无边界
类型擦除带来的问题, 无法通过 T t, t.method()的方法来进行代码编写
class Erick < T > {
private T t;
public Erick ( T t) {
this . t = t;
}
public void method ( ) {
t. wrok ( ) ;
}
}
2.2 extends边界
通过extends来告知编译器,只能接受遵循这个边界的类型 <T extends Animal>: T只能是 Animal的子类 类型擦除到了Animal, 就好像是用Animal代替了 T
package com. erick. day03 ;
public class Demo01 {
public static void main ( String [ ] args) {
Zoo < Dog > dogZoo = new Zoo < > ( new Dog ( ) ) ;
dogZoo. dailyLife ( ) ;
Zoo < Cat > catZoo = new Zoo < > ( new Cat ( ) ) ;
catZoo. dailyLife ( ) ;
}
}
class Zoo < T extends Animal > {
private T t;
public Zoo ( T t) {
this . t = t;
}
public void dailyLife ( ) {
t. eat ( ) ;
t. sleep ( ) ;
}
}
class Cat implements Animal {
@Override
public void eat ( ) {
System . out. println ( "猫吃鱼" ) ;
}
@Override
public void sleep ( ) {
System . out. println ( "猫打盹" ) ;
}
}
class Dog implements Animal {
@Override
public void eat ( ) {
System . out. println ( "狗吃肉" ) ;
}
@Override
public void sleep ( ) {
System . out. println ( "狗睡觉" ) ;
}
}
interface Animal {
void eat ( ) ;
void sleep ( ) ;
}
程序中的所有泛型类型,都将被擦除,替换为它们的非泛型上界 比如 List<T> 就会被擦除为 List 主要是为了迁移兼容性
五、多继承
边界的潜在的效果: 你可以按照自己的边界类型来调用方法 边界条件设置: 先是class,然后是interface 类只能继承一个,接口可以实现多继承
package com. erick. day04 ;
public class Demo01 {
public static void main ( String [ ] args) {
BigChina < People > bigChina = new BigChina < > ( new People ( ) ) ;
bigChina. allTheWay ( ) ;
}
}
class BigChina < T extends Animal & LivingThing & Earth > {
private T t;
public BigChina ( T t) {
this . t = t;
}
public void allTheWay ( ) {
t. eat ( ) ;
t. getAddress ( ) ;
t. life ( ) ;
}
}
class People extends Animal implements LivingThing , Earth {
@Override
public void life ( ) {
System . out. println ( "人类的生活" ) ;
}
@Override
public void getAddress ( ) {
System . out. println ( "人类住在房子里面" ) ;
}
@Override
public void eat ( ) {
System . out. println ( "人类要吃熟食" ) ;
}
}
class Animal {
public void eat ( ) {
System . out. println ( "动物吃东西" ) ;
}
}
interface LivingThing {
void life ( ) ;
}
interface Earth {
void getAddress ( ) ;
}
六、通配符
1. 数组的向上转型
错误的用了向上转型,因此导致在运行期会出现一定的问题
package com. erick. day04 ;
public class Demo02 {
public static void main ( String [ ] args) {
method ( ) ;
}
private static void method ( ) {
Fruit [ ] fruits = new Apple [ 5 ] ;
fruits[ 0 ] = new Apple ( ) ;
fruits[ 1 ] = new Fruit ( ) ;
}
}
class Fruit {
}
class Apple extends Fruit {
}
class ShanxiApple extends Apple {
}
泛型的出现,很重要的一个原因就是要把运行时候的问题,提升到编译时期 我们是在讨论容器的类型,而不是容器持有的类型
private static void method2 ( ) {
List < Fruit > list = new ArrayList < Apple > ( ) ;
}
2. 通配符的出现
如果向在两个类型之间建立某种类型的向上转型关系,可以引入通配符