Java学习day10

泛型

泛型(Generics)是Java中的一种特性,它允许在编写类和方法时使用类型参数,以在使用时指定具体的类型。泛型提供了编译时类型安全性,并且可以增加代码的重用性和可读性。

使用泛型的主要目的是在编译时捕获类型错误,并提供更好的类型检查和类型推断。通过使用泛型,可以在编译时检查代码中的类型错误,避免在运行时出现类型转换错误或其他类型相关的异常。

泛型(拆字为广泛的数据类型,也叫类型参数)即将类型进行一个参数化。

注意:

  • 这个参数是Java中的类型但不包括基本数据类型

  • 泛型类中的泛型不能应用到静态方法上面

泛型类

泛型类(Generic Class):可以在类的定义中使用类型参数,并在类的实例化时指定具体的类型。例如:

public class Box<T> {
    private T value;

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

    public T getValue() {
        return value;
    }
}

在上述示例中,Box是一个泛型类,T是类型参数。在实例化时,可以指定具体的类型,例如BoxBox等。

类型参数可以有多个,若在创建泛型类对象时没有传入参数,那么会默认编译为Object类型。

泛型接口

与泛型类基本相同。

泛型方法

泛型方法(Generic Method):可以在方法的定义中使用类型参数,并在方法的调用时指定具体的类型。例如:

public <T> T getValue(T[] array, int index) {
    return array[index];
}

在上述示例中,是一个类型参数,用于指定方法的返回类型。在调用方法时,可以指定具体的类型,例如getValue(array, 0)。如果没有指明,则会将T设置为类型参数共同的超类。

通配符

通配符(Wildcard):可以使用通配符来表示未知类型或类型的子类。例如:

public void processList(List<?> list) {
    for (Object element : list) {
        // 对列表中的元素进行处理
    }
}

在上述示例中,List表示一个未知类型的列表。这样可以接受任何类型的列表作为参数,但不能对列表中的元素做具体的类型假设。

在Java中,使用通配符(?)作为泛型类型参数,可以创建一个未知元素类型的List。在这种情况下,编译器无法确定List中元素的具体类型,只知道它是某种类型的List。

public static void test() {
    List<?> c = new ArrayList<String>();
    c.add(null);
}

在这个例子中,我们创建了一个未知元素类型的List,命名为c3,并使用ArrayList来实例化它。由于c3的元素类型未知,我们不能直接添加具体的元素对象,但是我们可以添加null作为通配符类型的元素。

c3.add(null)语句可以编译通过,因为null可以被赋值给任何引用类型,包括通配符类型。这样,null被添加到了c3列表中。

限定类型参数

限定类型参数(Bounded Type Parameters):可以使用限定类型参数来限制泛型类型的范围。例如:

public <T extends Number> double sum(T[] array) {
    double total = 0;
    for (T element : array) {
        total += element.doubleValue();
    }
    return total;
}

在上述示例中,表示类型参数T必须是Number类或其子类。这样可以确保在方法中调用Number类的方法,如doubleValue()

上界通配符

类型通配符上限通过形如List 来定义,表示只接受Number及其下层子类类型。

实例

public class GenericTest {
     
    public static void main(String[] args) {
        List<String> name = new ArrayList<String>();
        List<Integer> age = new ArrayList<Integer>();
        List<Number> number = new ArrayList<Number>();
        
        name.add("icon");
        age.add(18);
        number.add(314);
 
        //getUperNumber(name);//1
        getUperNumber(age);//2
        getUperNumber(number);//3
       
   }
 
   public static void getData(List<?> data) {
      System.out.println("data :" + data.get(0));
   }
   
   public static void getUperNumber(List<? extends Number> data) {
          System.out.println("data :" + data.get(0));
       }
}

输出结果:

data :18
data :314

解析://1 处会出现错误,因为 getUperNumber() 方法中的参数已经限定了参数泛型上限为 Number,所以泛型为 String 是不在这个范围之内,所以会报错。

下界通配符

类型通配符下限通过形如 List 来定义,表示类型只能接受 Number 及其上层父类类型,如 Object 类型的实例。

注意:规范上来说,上界通配符只读不写,下界通配符只写不读,原因在于,这里说的上界下界其实就是界限的意思。对于上界通配符,规定了上限,若向其写入null会报错;对于下界通配符,当使用get()获取返回值时,返回值类型为Object。

泛型擦除

泛型擦除是指泛型类型参数在编译时被擦除的过程。在Java中,泛型类型参数只存在于代码编译阶段,而在运行时它们被擦除并替换为它们的原始类型或限定类型。

泛型擦除是为了保持与Java早期版本的二进制兼容性和互操作性。由于Java的泛型是在Java 5中引入的,为了确保现有的非泛型代码可以与新的泛型代码一起使用,Java编译器在编译过程中会将泛型类型信息擦除。

例如:

public class MyGenericClass<T> {
    private T value;

    public T getValue() {
        return value;
    }

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

在编译过程中,编译器会将T擦除为其原始类型Object,因此编译后的代码将类似于以下形式:

public class MyGenericClass {
    private Object value;

    public Object getValue() {
        return value;
    }

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

这意味着在运行时,无法访问或使用泛型类型参数的具体类型信息。这种擦除限制了一些操作,例如无法在运行时创建泛型类型的实例,因为在运行时无法获得泛型类型参数的具体类型。

虽然泛型类型参数在运行时被擦除,但在编译过程中仍然进行了类型检查,以确保类型安全性。编译器在编译时会插入必要的类型转换代码,以确保在运行时不会出现类型错误。

你可能感兴趣的:(javaEE,java,学习,开发语言)