Java 泛型

泛型(generic)可以参数化类型。这个能力使我们可以定义带泛型类型的类或方法,随后编译器会用具体的类型来替换它。
Java定义了一个泛型类ArrayList用于存储泛型类型的元素。基于这个泛型类,可以创建出保存字符串或者数字的对象。这里的字符串和数字就是取代泛型类型的具体类型

泛型的动机和优点

从JDK 1.5开始,Java允许定义泛型类、泛型接口和泛型方法。

使用Java泛型的动机就是在编译时就会检测到错误,使程序更加可靠

Comparable c = new Date();
System.out.println(c.compareTO("red"));

在JDK 1.5之前这样编译时不会报错的,只有在运行时会产生错误。

Comparable<Date> c = new Data();
System.out.println(c.compareTo("red"));

JDK 1.5之后在编译时就会报错,泛型类型使程序更加可靠

例:

package java.lang
    
public interfact Comparable{
    public int compareTo(Object o);
}
a) JDK 1.5之前
package java.lang
    
public interfact Comparable<T>{
    public int compareTo(T o);
}
b) JDK 1.5之后

泛型类型必须是引用类型。不能使用int、double或char这样的基本类型来替换泛型类型

//这是错的
ArrayList<int> intList = new ArrayList<>();
//给int值创建一个ArrayList对象
ArrayList<Integer> intList = new ArrayList<>();
intList.add(15);
int i = intList.get(0);

Java会自动地将15包装为new Integer(15),这个过程称为自动打包(auto boxing),intList中的元素被赋给了int对象,intLIst(0)中的对象自动转化为基本类型的值。

定义泛型类

可以为类和接口定义泛型。当使用类来创建对象或者使用类或接口来声明引用变量时,必须指定具体类型

将一个存储对象的栈类修改,其元素类型通用化为泛型

GenericStack
-list:java.util.ArrayList一个数组列表,用于存储元素
+GenericStack()创建一个空栈
+getsSze(): int返回栈中的元素数目
+peek(): E返回栈顶元素
+pop(): E返回并移除栈顶元素
+push(o: E): void添加一个新的元素到栈顶
+isEmpty(): boolean如果栈为空,返回true

GenericStack类封装了栈的存储,并提供使用该栈的操作

public class GenericStack<E> {
    private ArrayList<E> list = new ArrayList<>();
    
    public int getSize(){
        retrun list.size;
    }
    
    public E peek(){
        retrun list.get(getSize()-1);
    }
    
    public E pop(){
        E o = list.get(getSize()-1);
        list.remove(getSize()-1);
        retrun 0;
    }
    
    public E push(E o){
        list.add(o);
    }
    
    public boolean isEmpty(){
        retrun list.isEmpty();
    }
    
    @Override
    public String toString(){
        retrun "stack:" + list.toString();
    }
}

创建栈时可以将在尖括号里面添加包装类型;可以不使用泛型,将元素类型设置成Object也可以容纳任何对象类型。但是使用泛型能够提高软件的可靠性和可读性,某些错误也可以在编译时被检测到

泛型方法

使用泛型类型来定义泛型方法。例如定义一个泛型方法print来打印一个对象数组

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

为了声明泛型方法,将泛型类型<E>方法写在方法头的关键字static之后

为了调用泛型方法,需要将实际类型放在尖括号中作为方法名的前缀或者简单调用。
可以将泛型指定为另外一种类型的子类型,这样的泛型类型称为受限的(bounded)。

public class BoundedTypeDemo{
    public static void main(String[] args){
        Rectangle rectangle = new Rectangle(2,2);
        Cricle cricle = new Cricle(2);
        //Rectangle和Cricle是GeometricObject的子类
        
        System.out.println("Same area?" + equalArea(rectangle,cricle));
    }
    
    public static <E extends GeometricObject> boolean equalArea(E object1,E object2){
        retrun object1.getArea() == object2.getArea();
    }
}

equalArea方法用来测试两个集合对象是否具有相同的面积,受限的泛型类型<E extend GeometricObject>将E指定为GeometricObject的泛型子类型。必须传递两个GeometricObject的实例来使用equalArea

  • 非受限泛型<E>相当于<E extends Object>
  • 为一个类定义泛型类型,需要将泛型类型放在类名之后;为一个方法定义类型,要将泛型类型放在返回类型之前。

通配泛型

...{...
    GenericStack<Integer> intStack = new GenericStack<>();
	intStack.push(1);
	intStack.push(2);
    
	System.out.println(max(intStack));//会在这里编译错误
   }

	public static double max(GenericStack<Number> stack){
        double max = stack.pop().doubleValue();
        
        while(!stack.isEmpty()){
            double value = stack.pop().doubleValue();
            if(value>max)
                max = value;
        }
    }

intStack 不是GenericStack < Number >的实例,因此不能调用max(),尽管Integer时Number的子类型,但是GenericStack不是GenericStack 的子类型,为了避免这个问题。可以使用统配泛型类型。统配类型有三种形式:?、? extends T、? super T,T是泛型类型。

  • 第一种形式 称为非受限统配,和? extends Object是一样的
  • 第二种形式 ? extends T 称为受限统配,表示T或者T的子类型
  • 第三种形式称为 ? super T称为下限统配,表示T或者T的一个父类型

那么将上面max方法修改以下:

public static double max(GenericStack<? extends Number> stack){...}

<? extends Number> 表示Number或者Number的子类型的通配类型

在方法中使用 ?通配符,表示任意一种对象类型。等价于<? extends Object>;如果用GenericStack替换为 GenericStack < ? >,调用方法会出错,Integer虽然是Object的子类型,凡是但是GenericStack< Integer >不是GenericStack< Object > 的子类型

<? super T>

...
//创建两个对象栈,假设stack1的类型为String,stack2的类型为Object
public static <T> void addd(GenericStack<T> stack1,GenericStack<? super T> stack2){
    while(!stack1.isEmpty()){
        stack.push(stack.pop());
    }
}

若是用< T >代替<? super T>,在调用add方法是会产生一个编译错误,stack1的类型是GenericStack< String>,stack2的类型是GenericStack< Object>. <? super T>表示类型T或者是T的父类性。Object是String的父类性

也可以将add方法写成:

public static <T> void add(GenericStack<? extends T> stack1,GenericStack<T> stack2){...}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值