转载

重识Java之夯实泛型

本文概要:

  1. 泛型的基本使用
    • 泛型类
    • 泛型接口
    • 泛型函数
  2. 泛型的进阶使用
    • 类型绑定
    • 通配符

泛型的基本使用

本节概要:

  • 泛型类,接口,函数的定义

泛型的优点:

  1. 不用强制转换
  2. 如果传入类型不对,编译时会报错

一. 泛型类的定义

1.1 单泛型变量类的定义

public class Location<T>{//标识符号可以随意写
    private T x ;      
    private T y ;      
    public void setX(T x){//作为参数
        this.x = x ;
    }
    public void setY(T y){
        this.y = y ;
    }
    public T getX(){//作为返回值
        return this.x ;
    }
    public T getY(){
        return this.y ;
    }
}
复制代码

1.2 多泛型变量类的定义

以 , 进行分隔

public class MoreLocation<T,U> {//标识符号可以随意写

    private T x;
    private U y;

    public void setX(T x) {//作为参数
        this.x = x;
    }

    public void setY(U y) {
        this.y = y;
    }

    public T getX() {//作为返回值
        return this.x;
    }

    public U getY() {
        return this.y;
    }

}

复制代码

1.3 标识符号的字母规范.

虽然标识符号可以随意取,但为了提高可读性,一般遵循以下规则.

  • E — Element,常用在java Collection里,如:List,Iterator,Set
  • K,V — Key,Value,代表Map的键值对
  • N — Number,数字
  • T — Type,类型,如String,Integer等等

二. 泛型接口的定义

在接口上定义泛型与在类中定义泛型是一样的

interface ILocation<T>{        // 在接口上定义泛型  
     T getZ() ;
     void setZ(T z);
}
复制代码

2.1 非泛型类实现泛型接口

public class LocationImpl implements ILocation<String>{
    private String z ;
    public LocationImpl(String z){	
        this.setZ(z) ;
    }
    
    @Override
    public void setZ(String z){
        this.z = z ;
    }
    
    @Override
    public String getZ(){
        return this.z ;
    }
}
复制代码

2.2 泛型类实现泛型接口

publick class LocationImpl<T,K,U> implements ILocation<U>{	//把第三个泛型变量U用来填充接口
     private U z ;	
     private T x;
     private K y;
     public LocationImpl(U z){	
         this.setZ(z) ;
     }
     
     @Override
     public void setZ(U z){
         this.z = z ;
     }
     
     @Override
     public U getZ(){
         return this.z ;
     }
 }

复制代码

三. 泛型函数的定义

在方法的返回值前加上来表示泛型变量.

public class MethodLocation {

    public static <T> void staticMethod(T text) {
        System.out.println("staticMethod:" + text);
    }
    
    //返回值与形参中存在泛型
    public <T> T normalMethod(T text) {
        System.out.println("normalMethod:" + text);
        return text;
    }

    //定义泛型数组, T[]相当于String[].
    public static <T> T[] funArray(T...arg){  // 接收可变参数  
        return arg ;            // 返回泛型数组  
    }
    
    
    public static void main(String[] args) {
       MethodLocation methodLocation = new MethodLocation();
        String text1 = methodLocation.normalMethod("hello");//方法1
        String text2 = methodLocation.<String>normalMethod("genericity");//方法2
        System.out.println("from main method:"+text1);
        System.out.println("--------------------------");
        Integer[] intArray = MethodLocation.funArray(1, 2, 3, 4, 5, 6);
        System.out.println(Arrays.toString(intArray));
    }
}

//输出结果
normalMethod:hello
normalMethod:genericity
from main method:hello
--------------------------
[1, 2, 3, 4, 5, 6]
复制代码

注意:虽然方法2是推荐方式,但实际在IDEA中会提示使用方法1,这是因为它会实时检测传入类型是否一致,不一致直接提醒编译不通过.

泛型的进阶使用

本节概要:

  1. 类型绑定
  2. 通配符

一. 类型绑定

在前一节中,T只能是Object的子类,所以在编译时只能调用Object类的相关方法.假如现在有个dog类,我们想在编译时就调用他父类Animal的方法.这时就需要用到类型绑定.

定义:

< T extends BoundingType >

T和BoundingType可以是类,也可以是接口.
此处的==extends==仅仅表示子类型,不等同于继承.

优点:

  1. 对泛型加以限定
  2. 泛型变量T,可以使用BoundingType内部的函数.
//省略....创建dog,Animal,IAction相关代码.

public class TypeBind {

    public static void main(String[] args) {
        Dog dog = new Dog("旺财");
        String nameClass = getAnimalNameClass(dog);
        System.out.println(nameClass);
        System.out.println("---------------");
        String actionInterface = getAnimalActionInterface(dog);
        System.out.println(actionInterface);
        System.out.println("---------------");
        String nameAction = getAnimalNameAndAction(dog, dog);
        System.out.println(nameAction);
    }

    //绑定类
    private static <T extends Animal> String getAnimalNameClass(T sub) {
        //这样编译时,就能调用Animal中的方法.
        return sub.getName();
    }

    //绑定接口
    private static <T extends IAction> String getAnimalActionInterface(T sub) {
        return sub.howl();
    }

    //多个泛型绑定多个类型,通过&符号指定多个类型
    private static <T extends Animal & Serializable, U extends IAction & Serializable> String
    getAnimalNameAndAction(T sub1, U sub2) {
        return sub1.getName() + sub2.howl();
    }
}
复制代码

二. 通配符

通配符的意义就是它是一个未知的符号,可以是代表==任意的类==,通常用?号表示.

作用:用于填充泛型变量T,表示代表任意类型!仅仅是填充方式的一种.

//使用位置有且只有,只能用来在生成泛型实例时使用.
Location<?> location;
location = new Location<Integer>();

复制代码

2.1 通配符?的extends绑定

  • 指填充为派生于XXX的任意子类的话.
  • 所以实例能取不能存.
Location<? extends Number> location;
location = new Location<Integer>(2,3);
//取
Number number = location.getX();//编译通过
//存
location.setX(new Integer(123);//编译报错
复制代码

location的类型由<? extends Number>决定,并不会因为location = new Location(2,3)而改变类型.所以不能确定通配符类型.

2.2 通配符?的super绑定

  • 则表示填充为任意XXX的父类.
  • 实例能存不能取.
/**
 * 准备三个类Animal(动物),Mammal(哺乳动物),Mouse(老鼠).
 * Mouse 继承 Mammal, Mammal 继承 Animal.
 * .....省略相关类代码
 */
 
 List<? super Mammal> list;
 list = new ArrayList<Animal>;
 //取
 Animal animal = list.get(0);//编译报错
 Object object = list.get(0);
 //存
 list.add(new Animal());//编译报错
 list.add(new Mammal());
 list.add(new Mouse());
复制代码

取的时候报错,编译器不能确定<? super Mammal>的父类就是Animal.

至于存的时候报错,同样因为是未知类型,编译器只能确定通配符类型为Mammal及其子类.

对于编译器,只要能确定通配符类型,就会编译通过.否则会报错.

最后的话:

本文只是对参考链接的内容总结,如需详细了解,请参考启舰大佬的博客链接.

由于本人技术有限,如有错误的地方,麻烦大家给我提出来,本人不胜感激,大家一起学习进步.

参考链接:

  • blog.csdn.net/harvic88092…
  • blog.csdn.net/harvic88092…
原文  https://juejin.im/post/5c1d00066fb9a04a0f6528f0
正文到此结束
Loading...