泛型的运用

泛型的定义

泛型是Java编程语言中的一种特性,其主要作用就是允许代码在编译时进行检查,如果发生错误会给出提示,从而提高了代码的类型安全性和可重用性。

泛型时JDK1.5推出的新特性,java允许定义泛型类型、泛型接口、泛型方法,javaAPI中对一些类和接口的使用进行了一些调整。

package java.lang;

public interface Comparable{

    public int compareTo(Object o);

}

package java.lang;

public interface Comparable<T>{

    public int compareTo(T o);

}

                         JDK1.5前                                                                      JDK1.5后

解释一下上述中<T> :  为形式泛型类型(formal generic type),可以来理解为一种“类型占位符”,就是我不确定你是什么类型,我先给你占个位置,到要使用的时候才能知道具体的类型。咋这个具体类型就叫做实际具体类型(actual concrete type)。替换泛型类型称为泛型实例化.(generic instantiation)。按照约定俗成惯例,像T或者E这样单个大写字母用于表示泛型类型。

注意:泛型类型时引用数据类型,而不是基本数据类型,不能使用int ,long或者char等基本数据类型来替换泛型类型。

泛型的优点

对于泛型的优点有以下几点:

1、类型安全   泛型确保了类型安全性,即在编译环节就进行错误检查。

2、代码重用   编写一次代码,就可以在多种类型数据上进行使用。

3、减少强制类型的转换   泛型消除在集合中使用强制转换类型的需求。

4、提高代码的可阅读性   泛型代码通常比非泛型代码更易于阅读和理解。

定义泛型类和接口

在java中,可以为类或者接口定义泛型,使用该类来创建对象(或者引用变量)时,必须使用具体的实际类型来替换它。

定义泛型类

public class ClassName<T1,T2,T3...,Tn>{

    //属性
    //方法
    ...

}

定义泛型接口

public interface InterfaceName<T1, T2, ..., Tn> {

    // 接口体

}

在上述的定义中,ClassName 为类名, InterfaceName 接口名,尖括号里面的:T1,T2,T3....Tn  就是泛型参数类型,他们可以是任何的类型,包括基本类型(不包含基本数据类型哦)和自定义类型。

下面使用泛型类和接口进行一个举例:

//定义泛型类
public class Box<T>{

    private T item;
    
    public void setItem(T item){
        this.item = item;

    }
    public T getItem(){
        return item;

    }

}

//定义泛型接口

public interface Storage<T>{
    void add(T item);
    T get(int index);
    int size();


}

对于泛型类和接口的使用

// 使用泛型类
Box<Integer> integerBox = new Box<>();
integerBox.setItem(10);
int value = integerBox.getItem(); // value 的类型是 Integer

// 使用泛型接口
Storage<String> stringStorage = new Box<>();
stringStorage.add("Hello");
stringStorage.add("World");

String firstString = stringStorage.get(0); // firstString 的类型是 String

在例子中,我们还使用了泛型方法,将在下面进行分析,不要着急。

值得注意的还是那句话:在创建对象时,或者使用类或者接口声明引用变量时,必须指明具体的数据类型(就像上述代码中的String  和 Integer )。

泛型方法

在java中,泛型方法可以允许你编写可以与任何数据类型一起工作的方法,具体定义泛型方法如下:

public <T> returnType methodName(T parameter) {
    // 方法体
}

在此方法中,<T>声明了一个类型参数T,它可以在方法返回类型和参数列表中使用。

public class GenericMethods {
    // 定义一个泛型方法
    public static <T> T getMiddleValue(T[] array) {
        return array[array.length / 2];
    }

    public static void main(String[] args) {
        // 使用泛型方法
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] stringArray = {"a", "b", "c", "d", "e"};

        Integer middleInt = getMiddleValue(intArray);
        String middleString = getMiddleValue(stringArray);

        System.out.println("Middle integer: " + middleInt);
        System.out.println("Middle string: " + middleString);
    }
}

在这个例子中,getMiddleValue方法是一个泛型方法,它接受一个泛型数组作为参数,并返回数组中间的元素。在main方法中,我们分别使用getMiddleValue方法处理整数数组和字符串数组,并打印出中间的元素。

可以将泛型指定为另外一种类型的子类型,这样的泛型类型称为受限的(bounded)。

public class BoundedTypeDemo {

  public static void main(String[] args ) {
    Rectangle rectangle = new Rectangle(2, 2);
    Circle circle = new Circle(2);

    System.out.println("Same area? " + equalArea(rectangle, circle));
  }

  public static <E extends GeometricObject> boolean equalArea(E object1, E object2) {
    return object1.getArea() == object2.getArea();
  }
}

原始类型和向后兼容

没有指定具体类型的泛型类或者泛型接口就是原始类型,用于早期的java版本,向后兼容。

GnericStack  stack  =  new  GnericStock( );     

                        等价于

                        

GnericStock <object>  stack  =  new  GnericStock< >( );

像这样不带类型参数的GnericStock 泛型称为原始类型(raw type),使用原始类型向后兼容java早期版本。

通配泛型

在Java中,统配泛型用于表示泛型类型的未知部分,允许我们在不指定具体类型的情况下使用泛型,有两种常见的通配泛型:

1、无界通配符(又叫非受限通配,unbounded wildcard)

        使用 ? 表示,他表示任何类型,通常用于方法参数,以便接受任何类型的对象。它和 ? extends Obiect 是一样的。

public void printList(List<?> list) {
    for (Object o : list) {
        System.out.println(o);
    }
}

 2、有界通配符

  • 上限受限通配(bounded wildcard)   使用<?  extends  T >表示它表示任何类型T 或者T的子类型。
    public void printList(List<? extends Number> list) {
        for (Number n : list) {
            System.out.println(n);
        }
    }
    
  •  下限受限通配(lower-bounded wildcard)使用< ?  super  T > 表示任何类型T 或者T的父类型。
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    // ...
}

泛型类型和通配类型之间的关系

消除泛型和对泛型的限制 

编译器可以使用泛型信息,但这些信息在运行时是不可用的。这被称为类型消除。

  泛型是使用一种叫做类型消除(擦除)的方法来实现的。编译器使用泛型类型信息来编译代码,但是随后会消除它。因此,泛型信息在运行时是不可用的。这种方法可以使泛型代码向后兼容使用原始类型的遗留代码。

  泛型存在于编译时。一旦编译器确认泛型类型是安全使用的,就会将它转换为原始类型。例如:编译器检查左边的代码里泛型是否被正确使用,然后将它翻译成右边的代码

ArrayList<String> list = new ArrayList<>();

list.add(“peppa”);

String name = list.get(0);

ArrayList list = new ArrayList();

list.add(“peppa”);

String name = (String)list.get(0);

  当编译泛型类、接口和方法时,编译器用Object类型代替泛型类型.如下翻译:

public static <E> void print(E[] list){

  for(int i = 0; i < list.length; i++)

     System.out.print(list[i] + “ “);

  System.out.println();

}

public static void print(Object[] list){

  for(int i = 0; i < list.length; i++)

    System.out.print(list[i] + “ “);

  System.out.println();

}

  如果一个泛型类型是受限的,那么编译器就会使用该受限类型来替换它。例如:

public static <E extends Geometric> boolean equalArea(E o1,E o2){

   return o1.getArea() == o2.getArea();

}

public static boolean equalArea(Geometric o1, Geometric o2){

   return o1.getArea() == o2.getArea();

}

  非常需要注意的是,不管实际的具体类型是什么,泛型类是被它的所有实例所共享的。

ArrayList<String> list1 = new ArrayList<>();

ArrayList<Integer> list2 = new ArrayList<>();

  尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有一个ArrayList类会被加载到JVM中。list1和list2都是ArrayList的实例。因此:

Sout:list1 instanceof ArrayList    //true

Sout:list2 instanceof ArrayList    //true

  然而表达式list1 instanceof ArrayList<String>是错误的。由于ArrayList<String>并没有在JVM中存储为单独一个类,所以,在运行时使用它是毫无意义的。

  由于泛型类型在运行时被擦除,因此,对于如何使用泛型类型是有一些限制的:

  限制1:不能使用new T()

    运行时泛型T是不可用的。

   限制2:不能使用new T[]

    可以通过创建一个Object的数组,然后将它的类型转换为T[]来规避这个限制,

    E[] elements = (E[])new Object[capacity];

    类型转换到E[]会导致一个免检的编译警告,因为编译器无法确保在运行时类型转换是否能成功,这种类型的编译警告是使用java泛型的不足之处,也是无法避免的。

  使用泛型类型创建泛型数组也是不允许的。

    GenericStack<String>[] stack = new GenericStack<String>[10];//错误的

  可以修改为下面代码来规避,但是会有编译警告

    GenericStack<String>[] stack = (GenericStack<String>[])new GenericStack [10];

  限制3:在静态上下文中不允许类的参数是泛型类型

  由于泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例所共享的。因此,在静态方法、数据域或者初始化的语句中,为类引用泛型类型参数是非法的。

  限制4:异常类不能是泛型的

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值