Java中的泛型

本文详细介绍了Java中的泛型,包括泛型类的定义、类型参数T的使用、子类泛型与父类的匹配、泛型接口的定义、泛型方法的特性、类型通配符的应用以及类型擦除的概念。特别关注了泛型在数组、继承和接口中的实践以及类型限定的上下限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、泛型类

(1)泛型类的定义语法

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

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

/**
 * @author xujin
 * @createtime 2021-02-14 14:29
 * @description
 * 泛型类的定义
 * <T>泛型标识-类型参数
 * 创建对象的时候指定具体的数据类型
 * 注意:
 * (1)泛型类在创建对象的时候,没有指定类型,将按照Object类型来操作
 * (2)泛型类的类型参数只能是类类型,不能是基本数据类型
 * (3)泛型类型在逻辑上可以看成是多个不同的类型,但其Class类型实际上都是相同类类型
 */

public class Generic<T> {
    private T value;

    public Generic(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

二、泛型类派生子类

(1)子类也是泛型类,子类和父类的泛型类型要一致

class ChildGeneric<T> extends Generic<T>
public class Parent<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

如果不声明父类的泛型,那么父类默认就是Object类型,如下:

public class ChildFirst<T> extends Parent {
    @Override
    public Object getValue() {
        return super.getValue();
    }
}

这是由于如果子类的泛型类型和父类的泛型类型不同的话,在调用子类的构造方法的时候就会报错,因为调用子类的构造方法里面会调用父类的构造方法,正确如下:

public class ChildFirst<T> extends Parent<T> {
    @Override
    public T getValue() {
        return super.getValue();
    }
}

也可以进行如下写法,但是要保证子类的泛型列表中要有一个泛型类型和父类一致,如下:

public class ChildFirst<T,E,K,V> extends Parent<T> {
    @Override
    public T getValue() {
        return super.getValue();
    }
}

(2)子类不是泛型类,父类要明确泛型的数据类型

class ChildGeneric extends Generic<String>
public class ChildSecond extends Parent<Integer> {
    @Override
    public Integer getValue() {
        return super.getValue();
    }
}

三、泛型接口

(1)泛型接口的定义语法

interface 接口名称<泛型标识,泛型标识,...> {
    泛型标识 方法名();
    ......
}

(2)实现类不是泛型类,接口要明确数据类型

(3)实现类也是泛型类,实现类和接口的泛型类型要一致。

四、泛型方法

(1)语法

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

(2)public与返回值中间的泛型列表非常重要,可以理解为声明此方法为泛型方法

(3)只有声明了泛型列表的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法

(4)泛型列表表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T

(5)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

public class GenericMethod {
    public static <E> E genericMethod(E value) {
        return value;
    }
}

(2)泛型方法与可变参数

public <E> void print(E... e) {
    for(E e1:e) {
        System.out.println(e);
    }
}
public static <E> void print(E... e) {
        for (int i = 0; i < e.length; i++) {
            System.out.println(e[i]);
        }
    }

(3)泛型方法总结
1)泛型方法能使方法独立与类而产生变化
2)如果static方法要使用泛型能力,就必须使使其成为泛型方法

五、类型通配符

(1)什么是类型通配符
1)类型通配符一般是使用“?”代替具体的类型实参
2)所以,类型通配符是类型实参。而不是类型形参

public class Box<E> {
    private E value;

    public E getValue() {
        return value;
    }

    public void setValue(E value) {
        this.value = value;
    }
    public static void showBox(Box<?> box) {
        Object value = box.getValue();
    }
}

(2)类型通配符的上限
语法:

/接口<? extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型

public static void showBox(Box<? extends Number> box) {
        Number value = box.getValue();
    }

有类型通配符上限的集合是不能添加元素的
在这里插入图片描述

(3)类型通配符的下限
语法:

/接口<? super 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型

public static void showBox(Box<? super Number> box) {
        Object value = box.getValue();
    }

而有类型通配符下限的集合是能添加元素的,但不能保证数据的正确性
在这里插入图片描述

六、类型擦除

  泛型是Java1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,于泛型相关的信息会被擦除掉,我们称为--类型擦除

(1)无限制类型擦除
在这里插入图片描述

public class Erasure<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

泛型发生在生产字节码文件的时候,我们用反射来看一下字节码文件中value是什么类型的:

public class Test {
    public static void main(String[] args) {
        Erasure<Integer> erasure = new Erasure<>();
        Class<? extends Erasure> erasureClass = erasure.getClass();
        Field[] declaredFields = erasureClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName()+":"+declaredField.getType().getSimpleName());
        }
    }
}

运行结果如下:
在这里插入图片描述
(2)有限制的类型擦除
在这里插入图片描述

public class Erasure<T extends Number> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

运行结果:
在这里插入图片描述

七、泛型与数组

(1)可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象,这是因为泛型是在编译器做类型擦除,而数组会在整个编译器去持有相应的数据类型:
在这里插入图片描述
不过可以这样创建:
在这里插入图片描述

(2)可以通过java.lang.reflect.Array的newInstance(Class, int)创建(泛型)T[]数组

public class Fruit<T> {
    private T[] array;

    public Fruit(Class<T> clz, int size) {
        array = (T[]) Array.newInstance(clz, size);
    }

}

<think>好的,我现在需要帮助用户了解Java的使用方法和示例。首先,用户提供的引用内容提到类、方法、静态方法的以及伪的概念。我需要结合这些信息,整理出一个结构清晰的回答,并确保遵循系统级指令中的格式要求。 首先,用户可能对的基本概念不太清楚,所以需要先简单解释什么是,以及它的作用,比如类安全、代码复用等。然后,分点介绍的不同用法,比如类、方法、接口,每个部分都要有示例代码,并且代码中的数学表达式要用$...$格式,比如在解释方法时,如果有涉及类参数T,可以用$T$来表示。 接下来,需要注意引用内容中提到的伪,说明Java在编译时类擦除的特性,这可能会影响运行时无法获取的具体类。此外,用户可能想知道如何使用通配符和边界,比如上界和下界通配符,这部分需要给出示例,并正确应用LaTeX格式,比如$? extends Number$。 用户还提到静态方法的,需要特别注意静态方法不能使用类定义的,而必须自己声明,这点要强调,避免常见错误。示例代码需要正确展示如何在静态方法中定义类参数。 最后,生成相关问题时要考虑用户可能进一步想知道的内容,比如类擦除的影响、通配符的使用场景、与集合框架的结合,以及类边界的作用。确保每个问题都有针对性,帮助用户深入理解。 现在检查是否有遗漏,比如是否覆盖了所有引用中的要点,引用的标识是否正确添加,示例代码是否符合语法,数学表达式是否正确使用$符号。同时,确保回答结构清晰,分点明确,符合用户可见层的要求。</think>### Java使用方法及示例 Java用于实现**类参数化**,提高代码复用性和类安全性[^1]。以下是具体用法及示例: --- #### 一、类 定义类时声明类参数$T$,可在类中作为类使用: ```java public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } // 使用示例 Box<String> stringBox = new Box<>(); stringBox.setContent("Hello"); ``` --- #### 二、方法 在方法返回值前声明类参数$T$,可在非类中使用: ```java public class Utils { public static <T> T getMiddle(T[] arr) { return arr[arr.length/2]; } } // 调用示例 String[] names = {"Alice", "Bob", "Charlie"}; String middle = Utils.<String>getMiddle(names); // 返回"Bob" ``` --- #### 三、接口 定义接口时声明类参数,常用于集合类: ```java public interface List<T> { void add(T element); T get(int index); } // 实现示例 class StringList implements List<String> { // 必须使用String类实现方法 } ``` --- #### 四、类边界 限制范围: ```java public static <T extends Number> double sum(T a, T b) { return a.doubleValue() + b.doubleValue(); } // 只能传入Number及其子类 sum(10, 20.5); // 合法 sum("a", "b"); // 编译错误 ``` --- #### 五、通配符 处理未知类集合时使用: ```java // 上界通配符 void process(List<? extends Number> list) { /* 可读不可写 */ } // 下界通配符 void addNumbers(List<? super Integer> list) { /* 可写不可读 */ } ``` --- #### 关键特性 1. **类擦除**:编译后会被替换为Object或边界类[^3] 2. **静态方法限制**:静态方法需单独声明: ```java public static <T> void print(T item) { System.out.println(item); } ``` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值