泛型

2021-05-17  本文已影响0人  Amy木婉清

1.泛型类的定义

泛型的实质就是一个类型占位符(T W E均可以代替),或指定其类型,如String Integer等。
List源码中占位符如下图:


image.png

不知道传什么类型,用泛型来占位。
List后就是一个泛型,E表示占位,即传什么类型就是什么类型。


image.png
例如:E的位置传的是string,那就是string类型,后面的泛型可写可不写,因为会根据前面指定的泛型自动匹配。
image.png
泛型的类型传string后,调用add方法只能add String类型
image.png

泛型的类型传Integer后,调用add方法只能add Integer类型


image.png
故:泛型类型传什么类型,就只能使用什么类型。
泛型是为了确定数据添加的指定类型而加入的。
package com.example.fxtest;

/**
 * @author zwp
 * @description:  确定属性的类型时可直接给定
 */
public class Person {
    private String name;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("zhangsan");
        System.out.println(person.getName());
    }
}

运行结果:zhangsan
泛型类:在不知道未来属性类型时,可以使用类型占位符来占位,这时定义一个泛型类,即把代码中所有特定类型换成类型占位符。

package com.example.fxtest;

/**
 * @author zwp
 * @description: 如果说在不知道未来name和address类型的时候,可以使用类型占位符来进行占位
 * 定义一个泛型类
 * 定义泛型类实质就是将现在代码中所有的特定类型换成类型占位符
 */
public class FxPerson<T> {
    private T name;
    private T address;

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }

    public T getAddress() {
        return address;
    }

    public void setAddress(T address) {
        this.address = address;
    }
}
public class Test {
    public static void main(String[] args) {
        FxPerson<String> fxPerson = new FxPerson<>();
        fxPerson.setName("lisi");
        System.out.println(fxPerson.getName());
    }
}

运行结果:lisi
泛型类型传递什么类型,使用的时候只能用什么类型。如下图所示:


image.png
image.png

多个类型的泛型类:

package com.example.fxtest;

/**
 * @author zwp
 * @description: 传多个参数泛型类
 */
public class FxPersonMore<K,V> {
    private K name;
    private V address;

    public K getName() {
        return name;
    }

    public void setName(K name) {
        this.name = name;
    }

    public V getAddress() {
        return address;
    }

    public void setAddress(V address) {
        this.address = address;
    }
}
public class Test {
    public static void main(String[] args) {
        //多个类型的泛型类
        FxPersonMore<String,Integer> fxPersonMore = new FxPersonMore<>();
        fxPersonMore.setName("wangwu");
        fxPersonMore.setAddress(123);
        System.out.println(fxPersonMore.getAddress());
        System.out.println(fxPersonMore.getName());
    }
}

运行结果:123
wangwu

传递什么类型,使用什么类型:


image.png

2.泛型方法

2.1 泛型普通方法

给类指定一个泛型,通过类的泛型确定方法参数的泛型

package com.example.fxtest;

/**
 * @author zwp
 * @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
 */
public class FxPersonM<T> {
    public void show(T name){
        System.out.println(name+"正在演讲!");

    }
}
package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛方法
 */
public class TestFxMethod {
    public static void main(String[] args) {
        FxPersonM<String> fxPersonM = new FxPersonM<>();
        fxPersonM.show("heiliu");
    }
}

运行结果:heiliu正在演讲!

2.2 泛型静态方法

静态泛型方法中的类型占位符和类中的泛型占位符是没有关系的,如下图:


image.png

未经过标识的占位符不可使用,如下图



静态方法中的占位标识符在使用前必须经过标识,如下图:
image.png

泛型静态方法示例:

package com.example.fxtest;

/**
 * @author zwp
 * @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
 */
public class FxPersonM<T> {
    /**
     * 2.泛型静态方法
     * 静态泛型方法中的类型占位符和类中的泛型占位符是没有关系的
     * @param name
     */
    public static<W> void show1(W name){
        System.out.println(name+":静态方法正在演讲!");
    }
}
package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型方法
 */
public class TestFxMethod {
    public static void main(String[] args) {
        // 2.泛型静态方法
        FxPersonM.show1("lisa");
        FxPersonM.show1(123);
    }
}

运行结果:
lisa:静态方法正在演讲!
123:静态方法正在演讲!
有返回值的静态泛型方法:

package com.example.fxtest;

/**
 * @author zwp
 * @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
 */
public class FxPersonM<T> {
    /**
     * 有返回值类型的静态泛型方法占位符
     * @param name
     * @param <E> 占位符
     * @return
     */
    public static<E> E show2(E name){
        return name;
    }
}
package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型方法
 */
public class TestFxMethod {
    public static void main(String[] args) {
        //有返回值的静态泛型方法
        System.out.println(FxPersonM.show2("David"));
    }
}

运行结果:David

3.泛型方法补充说明:

如果你想让普通方法的类型不与类方法的类型一致,该怎么实现呢?
例如:类方法的泛型占位符为外部传入的任意类型,但普通方法只想要用String类型
只要是方法,就可以自定义泛型占位符

package com.example.fxtest;

/**
 * @author zwp
 * @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
 */
public class FxPersonM<T> {
    //3.普通方法自定义占位符
    public <W> void selfShow(W name) {
        System.out.println(name + "正在演讲!");
    }
}
image.png
 fxPersonM.selfShow("alic");
 fxPersonM.selfShow(45);

运行结果:
alic正在演讲!
45正在演讲!

4.泛型接口

定义一个泛型接口,不指定具体泛型类型

package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型接口
 */
public interface PerInterface<T>{
    public void show(T name);
}

定义一个实现泛型接口的类;实现泛型接口,将泛型接口中占位符指定为某一类型。

package com.example.fxtest;

/**
 * @author zwp
 * @description: 实现泛型接口的类
 * 1.实现泛型接口,将泛型接口中占位符指定为某一类型
 * 2.泛型接口的实现类可以指定具体的泛型接口的具体泛型的类型
 */
public class PerIntImpl implements PerInterface<String>{

    @Override
    public void show(String name) {
        System.out.println(name);
    }
}

测试实现泛型接口的类

package com.example.fxtest;

/**
 * @author zwp
 * @description: 测试实现泛型接口的类
 */
public class TestInterface {
    public static void main(String[] args) {
        PerIntImpl perInt = new PerIntImpl();
        perInt.show("li");
    }
}

运行结果:li

定义一个实现泛型接口的类;实现泛型接口,不指定泛型类型。

//泛型接口的实现类如果没有指定具体的泛型,必须要在这个实现类中声明一个泛型给泛型类型
//这种情况外部调用show方法可以传任意类型
public class PerIntImpl<T> implements PerInterface<T>{

    @Override
    public void show(T name) {
        System.out.println(name);
    }
}

5.泛型擦除模式

定义一个泛型类

package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型擦除模式
 */
public class FxPerErase<T> {
    private T name;
    public  void show(){
        System.out.println("方法被调用");
    }
}

测试类:

package com.example.fxtest;

/**
 * @author zwp
 * @description: 擦除模式
 */
public class TestEras {
    public static void main(String[] args) {
        FxPerErase<String> fxPerErase = new FxPerErase<>();
        FxPerErase<String> fxPerErase1 = new FxPerErase<>();
        System.out.println(fxPerErase.getClass()==fxPerErase1.getClass());
    }
}

运行结果:true
如果把fxPerErase1前面的类型变成Integer,结果还是true。
即:

  FxPerErase<Integer> fxPerErase1 = new FxPerErase<>();

此时就涉及到一个名词叫擦除模式
Java中的泛型只存在于编码编译阶段,在运行期间泛型的类型是会被去除掉的。
擦除模式实质就是在代码运行期间将所有的泛型全部去掉
Ques:为什么要使用擦除模式?
Ans:为了兼容jdk老版本的编码

6.泛型通配符

(Integer是Number的子类)


image.png

Java中的继承并不是泛型中的继承
Java中的父子类关系 在泛型中 并不是父子类关系,示例如下图所示:


image.png
解决上述问题,使用通配符
package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型擦除模式
 */
public class FxPerErase<T> {
    private T name;

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }

    public  void show(FxPerErase<?> fxPerErase){
        this.setName((T)fxPerErase.getName());
    }
}
image.png
package com.example.fxtest;

/**
 * @author zwp
 * @description: 擦除模式
 * 通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况下是被认可的,所以要使用通配符来进行处理
 * 会使用通配符在泛型中将java中的继承关系 重新绑定
 * 通配符一般使用?来表示  可以理解为?是泛型中所有类的父类
 * 通配符的上边界  下边界问题
 * java中的继承 并不是泛型中的继承
 * java中的父子类关系  在泛型中 并不是父子类关系
 */
public class TestEras {
    public static void main(String[] args) {
        FxPerErase<Number> fxPerErase = new FxPerErase<>();
        FxPerErase<Integer> fxPerErase1 = new FxPerErase<>();
        FxPerErase<String> fxPerErase2 = new FxPerErase<>();
        fxPerErase1.setName(111);
        fxPerErase.show(fxPerErase1);//fxPerErase fxPerErase1 fxPerErase2均可
        System.out.println(fxPerErase.getName());
    }
}

运行结果:111

7.泛型上边界 extends 下边界 super 关键字

7.1.? extends T 代表的是泛型可以传入T和T的子类的类型
package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型擦除模式
 */
public class FxPerErase<T> {
    private T name;

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }
      //? extends T 代表的是泛型可以传入T和T的子类的类型
    public  void show(FxPerErase<? extends T> fxPerErase){
        this.setName((T)fxPerErase.getName());
    }
}

package com.example.fxtest;

/**
 * @author zwp
 * @description: 擦除模式
 * 通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况下是被认可的,所以要使用通配符来进行处理
 * 会使用通配符在泛型中将java中的继承关系 重新绑定
 * 通配符一般使用?来表示  可以理解为?是泛型中所有类的父类
 * 通配符的上边界 extends  下边界问题 super 关键字
 * java中的继承 并不是泛型中的继承
 * java中的父子类关系  在泛型中 并不是父子类关系
 */
public class TestEras {
    public static void main(String[] args) {
        FxPerErase<Number> fxPerErase = new FxPerErase<>();
        FxPerErase<String> fxPerErase2 = new FxPerErase<>();
        fxPerErase2.setName("2222");
        fxPerErase.show(fxPerErase2);//fxPerErase fxPerErase1 fxPerErase2均可
        System.out.println(fxPerErase.getName());
    }
}

运行结果:2222

7.2.? super T 代表的是泛型可以传入T和T的父类
package com.example.fxtest;

/**
 * @author zwp
 * @description: 泛型擦除模式
 */
public class FxPerErase<T> {
    private T name;

    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }
    //? super T 代表的是泛型可以传入T和T的父类
    public  void show(FxPerErase<? super T> fxPerErase){
        //System.out.println("方法被调用");
        this.setName((T)fxPerErase.getName());
    }
}
package com.example.fxtest;

/**
 * @author zwp
 * @description: 擦除模式
 * 通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况下是被认可的,所以要使用通配符来进行处理
 * 会使用通配符在泛型中将java中的继承关系 重新绑定
 * 通配符一般使用?来表示  可以理解为?是泛型中所有类的父类
 * 通配符的上边界 extends  下边界问题 super 关键字
 * java中的继承 并不是泛型中的继承
 * java中的父子类关系  在泛型中 并不是父子类关系
 */
public class TestEras {
    public static void main(String[] args) {
        FxPerErase<Number> fxPerErase = new FxPerErase<>();
        FxPerErase<Integer> fxPerErase1 = new FxPerErase<>();
        FxPerErase<String> fxPerErase2 = new FxPerErase<>();
        fxPerErase1.setName(111);
        fxPerErase2.setName("2222");
        fxPerErase.show(fxPerErase);//super 下边界
        System.out.println(fxPerErase.getName());
    }
}
7.3.什么时候用上边界 什么时候用下边界

1.上边界 在读取T这个类型数据的时候,但不写入数据的时候使用上边界
2.下边界 需要写入数据的时候,但不需要读取的时候使用下边界
3.既要读又要写不使用通配符

上一篇 下一篇

猜你喜欢

热点阅读