JAVA入门基础_JAVA中的泛型 Generic(六)

泛型Generic

什么是泛型、为什么需要使用泛型

  •   泛型:泛型是JDK1.5引入的一个新特性,提供了编译时类型安全的监测极致,该极致允许我们在编译时期监测到非法的类型数据结构。本质上就是一个是一个参数化类型,理解为要把所操作的类型当做参数来使用。

  •   为何要使用: 在JDK1.5没有推出泛型以前,我们如果一个方法想要接收多种不同类型的参数,就只能使用Object来作为参数类型。也就导致了频繁的强制转换,容易发生类型转换异常

    1. 因此使用泛型有保证数据安全的作用

    2. 消除了强制类型的转换(生成的字节码文件会自动进行强制转换)

泛型的常用标识(T、E、K、V)

  • T 代表type类型,一般是指JAVA类
  • E 代表element元素,一般是指的集合中的元素类型
  • K 理解为map中的key,键
  • V 理解为map中的value,值

泛型的知识点(未指定时、泛型类型)

  • 当一个泛型类或泛型方法的泛型未被指定时,会直接使用Object类型

  • 泛型的参数类型只能是类类型,不能是基本数据类型(基本数据类型不继承Object)

  • 泛型类型从逻辑上可以看成是多个不同的类型,但实际上都是相同类型(可以看看如下的示例)

public class Test {
    public static void main(String[] args) {
        Generic<Integer,Integer> generic1 = new Generic<>();
        Generic<String,String> generic2 = new Generic<>();

        // 可以看出,泛型类型实际上都是相同类型
        System.out.println(generic1.getClass()); // Generic
        System.out.println(generic2.getClass()); // Generic
        System.out.println(generic2.getClass() == generic1.getClass()); // true
    }
}
/**
 * 定义一个泛型类
 */
class Generic<T,E> {
}

泛型类

泛型类的语法

class 类名称 <泛型标识, 泛型标识> {
	private 泛型标识 变量名;
	
	private 泛型标志 show(泛型标识 变量名) {...}
}

泛型类的定义与使用

  • 创建该类的对象即可使用泛型类了,示例如下:
/**
 * 定义一个泛型类
 * @param <T>  一个泛型标识
 * @param <E>  一个泛型标识
 */
class Generic<T,E> {
    private T t;
    private E e;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public E getE() {
        return e;
    }

    public void setE(E e) {
        this.e = e;
    }
}
public class Test {
    public static void main(String[] args) {
        Generic<String,Integer> generic = new Generic<>();
        generic.setT("张三");
        generic.setE(159);

        System.out.println(generic.getT()); // 张三
        System.out.println(generic.getE()); // 159
    }
}

泛型类的子类是泛型类时

  • 先定义一个泛型类
class GenericFather<T> {
    
}
  • 定义泛型类型的子类
// 如果需要标识父类的泛型类型,则**子类的泛型标识列表**中必须含有为其父类标识的泛型
class GenericSon<T, E> extends GenericFather<T>{

}

或者也是可以正确编译的

class GenericSon<T, E> extends GenericFather<E>{

}

泛型类的子类不是泛型类时(那么这就可以当成是一个普通的类了)

// 为继承的父类传入泛型的参数类型,不传则默认为Object类型
class GenericSon extends GenericFather<String>{

}

泛型接口(与泛型类使用方法一致)

泛型接口的定义语法

注意:接口中无法定义带泛型的变量,例如: T name;

interface 接口名称 <泛型标识, 泛型标识> {
	泛型标志 show(泛型标识 变量名);
}

/**
 * 一个泛型接口
 * @param <T> 泛型标识
 */
interface GenericInterface<T> {
    void show(T t);
}

泛型接口的使用(这里使用了匿名内部类,搞个子类实现一下也可以的)

public class Test {
    public static void main(String[] args) {
        GenericInterface<String> genericInterface = new GenericInterface<String>() {

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

        genericInterface.show("123");

    }
}

/**
 * 一个泛型接口
 * @param <T> 泛型标识
 */
interface GenericInterface<T> {
    void show(T t);
}

泛型接口的实现类为泛型时

// 如果需要标识实现类的泛型类型,则**实现类的泛型标识列表**中必须含有为其实现接口标识的泛型
class GenericSon<T> implements GenericInterface<T>{
    
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}


interface GenericInterface<T> {
    void show(T t);
}

泛型接口的实现类不为泛型时

// 可以为接口的泛型传入参数类型,不传则默认为Object
class GenericSon implements GenericInterface<String>{

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


interface GenericInterface<T> {
    void show(T t);
}

泛型方法

泛型方法的定义语法

修饰符 <泛型标识, 泛型标识, ...> 返回值类型 方法名(形参列表){
	方法体...
}

泛型方法的使用

public class Test {
    public static void main(String[] args) {
        Generic generic = new Generic();

        // 1. 调用一下方法,随便传入2个类型的值
        Integer number = generic.show("abcde", 123);

        System.out.println(number); // 123

    }
}

class Generic {
    /**
     *
     * @param k 调用时传入的类型,会直接匹配泛型标识列表中的K
     * @param v 调用时传入的类型,会直接匹配泛型标识列表中的V
     * @return 返回一个V类型的
     */
     <K,V> V show(K k,V v){
         System.out.println(k); // abcde
         return v;
    }
}

静态泛型方法(可以通过类名点的方式调用)

    static <T> void staticGenericMethod(T t) {
        System.out.println(t);
    }

泛型方法的使用细节(泛型、非泛型方法、独立于类、静态方法如何获取泛型能力、优先原则)

  • 并不是使用了泛型作为返回值类型或形参的类型就称之为泛型方法(此时只能称为成员方法)

  • 返回值类型前加了泛型标识的列表就称之为泛型方法

  • 泛型方法能够独立于类而发生变化(泛型类定义的泛型标识跟泛型方法定义的泛型标识独立分开的,即便标识名一致

  • 如果静态方法需要具备泛型能力,则需要成为一个泛型方法(静态方法无法使用当前类所标识的泛型)

  • 如果一个泛型方法定义的泛型标识与类一致,会优先使用泛型方法上定义的泛型类型。
    image

类型通配符、上限、下限

类型通配符的使用(相当于是一个实参,可以将原本要传入的泛型实参代替)

  • 类型通配符为一个问号: ?,可以表示任意泛型,相当于所有泛型的父类,作为实参使用

  • 先看如下代码,method1(box2);这一行代码编译错误无法使用。

public class Test {
    public static void main(String[] args) {
        Box<Number> box1 = new Box<>();
        Box<Double> box2 = new Box<>();
        
        // 可以通过传入box1来调用method1方法
        method1(box1);
        
        // 无法通过传入box2来调用method1方法
        method1(box2);

    }

    public static void method1(Box<Number> box){
        box.get();
    }

}

class Box<T> {
    void get() {
        System.out.println("哈哈");
    }
}
  • 因此可以将mehtod1代码改成如下
    public static void method1(Box<?> box){
        box.get();
    }

指定所传入泛型类型的范围下限<? extends 类型>

    /**
     * 只能传入存储类型为Cat或者Cat的子类的ArrayList
     * @param list
     */
    public static void get(ArrayList<? extends Cat> list) {
    }

指定所传入泛型类型的范围上限<? super 类型>

    /**
     * 只能传入存储类型为Cat或者Cat的父类的ArrayList
     * @param list
     */
    public static void get(ArrayList<? super Cat> list) {
    }

类型擦除(泛型能够很好的兼容JDK1.5之后版本的原因)

无限制的代码擦除(泛型类、泛型方法):(底层会帮助进行强制类型转换)

泛型类型都会被转换为Objcet类

有限制的代码擦除(泛型类、泛型方法),也就是加了下限时

泛型类型都会被转换为下限的类型,例如<? extends Number>,那么泛型都会转换为Number类型。

桥接方法

Test测试代码

public class Test {
    public static void main(String[] args) {
        Son<String> son = new Son<>();
        son.show("张三");
    }

}

编译前的实现类与接口

class Son<T> implements Father<T> {
    
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

interface Father<T> {
    void show(T t);
}

编译后的实现类与接口(实现类增加方法)

class Son implements Father {

    // 转换为Object类型,达到与父类一致,主要是为了重写父类方法防止报错。
    @Override
    public void show(Object t) {
        System.out.println(t);
    }

    // 会添加一个方法,类型为String类型
    public void show(String t) {
        System.out.println(t);
    }
}

interface Father {
    void show(Object t);
}

创建一个泛型数组(不建议使用)

public class Test {
    public static void main(String[] args) {
        Generic<String> generic = new Generic<>();
        
        String[] strings = generic.get(String.class, 5);
        
        strings[0] = "张三";
        System.out.println(strings[0]);
    }

}

class Generic<T> {
    private T[] arr;

    public T[] get(Class<T> clazz, int length) {
        arr = (T[]) Array.newInstance(clazz, length);
        return arr;
    }
}
posted @   CodeStars  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示