泛型的介绍和使用二

1 类型绑定extends

为什么要适用类型绑定?

public interface Comparable<T>{
    public boolean compareTo(T i);
}

但如果我们直接通过函数来比较类型的大小,肯定会报错,因为编译器根本不知道什么类型?又怎么比较呢?

所以需要让编译器知道是什么类型,这就是类型绑定的用处了。

定义形式:

<T extends BoundingType>

此定义表示T应该是BoundingType的子类型(subtype)。T和BoundingType可以是类,也可以是接口。另外注意的是,此处的”extends“表示的子类型,不等同于继承。

类型绑定有两个作用:1、对填充的泛型加以限定 2、使用泛型变量T时,可以使用BoundingType内部的函数。

(1)绑定接口

public interface Comparable<T> {
    public boolean compareTo(T i);
}
//添加上extends Comparable之后,就可以Comparable里的函数了
public static <T extends Comparable>  T min(T...a){
    T smallest = a[0];
    for(T item:a){
        if (smallest.compareTo(item)){
            smallest = item;
        }
    }
    return smallest;
}

这里的泛型直接绑定Comparable接口,通过传进去的类型数组a,然后比较每一项的大小,最后得到最小值。

public class StringCompare implements Comparable<StringCompare> {
    private String mStr;
 
    public StringCompare(String string){
        this.mStr = string;
    }
 
    @Override
    public  boolean compareTo(StringCompare str) {
        if (mStr.length() > str.mStr.length()){
            return true;
        }
        return false;
    }
}

以上是非泛型类实现泛型接口,所以要填充类型,很多人可能会发现接口填充为什么是StringCompare,这是由于StringCompare绑定了Comparable接口,那么填充的时候也要用StringSompare类型来填充,最后是调用函数:

StringCompare result = min(new  StringCompare("123"),new StringCompare("234"),new StringCompare("59897"));
Log.d(TAG,"min:"+result.mStr);

(2)绑定类型

假设我们有一个水果摊,有许多水果,需要打印出填充进去的水果

class Fruit {
    private String name;
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

通过泛型函数来提取名字

public static <T extends Fruit> String getFruitName(T t){
    return t.getName();
}

已知水果都会继承Fruit类

class Banana extends Fruit{
    public Banana(){
        setName("bababa");
    }
}
class Apple extends Fruit{
    public Apple(){
        setName("apple");
    }
}

同过<T extends Fruit>就可以限定填充进去必定是Fruit的子类了。

最后调用

String name_1 = getFruitName(new Banana());
String name_2 = getFruitName(new Apple());
Log.d(TAG,name_1);
Log.d(TAG,name_2);

也可以绑定多个限定

public static <T extends Comparable & Serializable, U extends Runnable> T foo(T a, U b){
	…………
}

2 通配符

什么是通配符?首先还是引入类型Point<T>

class Point<T> {
    private T x;      
    private T y;      
    
    public Point(){
        
    }
    public Point(T x,T y){
        this.x = x;
        this.y = y;
    }
 
    public void setX(T x) {
        this.x = x;
    }
 
    public void setY(T y) {
        this.y = y;
    }
 
    public T getX() {
        return this.x;
    }
 
    public T getY() {
        return this.y;
    }
}

然后生成四个实例

Point<Integer> integerPoint = new Point<Integer>(3,3);
…………
Point<Float> floatPoint = new Point<Float>(4.3f,4.3f);
…………
Point<Double> doublePoint = new Point<Double>(4.3d,4.90d);
…………
Point<Long> longPoint = new Point<Long>(12l,23l);
…………

生成四个实例需要4个不同的变量,有没有一种方法可以只生成一个变量,然后将不同类型的实例赋值给它。

看下实现方式

Point<?> point;
 
point = new Point<Integer>(3,3);
point = new Point<Float>(4.3f,4.3f);
point = new Point<Double>(4.3d,4.90d);
point = new Point<Long>(12l,23l);

这里的?就是无边通配符。它代指任意符号,可代表任意的类。

无边通配符只能用于填充泛型T,表示统配任何类型。不能用于定义变量。

Box<?> box;
box = new Box<String>();

3 通配符的绑定

和泛型绑定类似,如果不加以限定,在后期的使用中编译器可能不会报错。所以我们同样,要对?加以限定。

 Point<? extends Number> point;

当将T填充为String和Object时,赋值给point就会报错!
这里虽然是指派生自Number的任意类型,但大家注意到了没: new Point<Number>();也是可以成功赋值的,这说明包括边界自身。
再重复一遍:无边界通配符只是泛型T的填充方式,给他加上限定,只是限定了赋值给它(比如这里的point)的实例类型。
如果想从根本上解决乱填充Point的问题,需要从Point泛型类定义时加上<T extends Number>:

class Point<T extends Number> {
    private T x;       // 表示X坐标
    private T y;       // 表示Y坐标
 
   …………
}

注意:

 Point<? extends Number> point;
 point = new Point<Integer>(3.33);
 Number number = point.getX();
 point.setX(new Integer(222));

第四行会报错,为什么呢?因为填充Point的泛型变量T是<? extends Number>,这是一个未知类型,不能用来设置內部值。

但是取值的时候,由于泛型变量T被填充为<? extends Number>,所以编译器能确定T是Number的子类,编译器就回用Number来填充。

4 通配符?的super绑定

<? extends Number> 指的是被填充为Number的子类,那么<? super Number> 就是指填充T为Number的父类。

class CEO extends Manager {
}
 
class Manager extends Employee {
}
 
class Employee {
}
List<? super Manager> list;
list = new ArrayList<Employee>();
//存
list.add(new Employee()); //编译错误
list.add(new Manager());
list.add(new CEO());

为啥添加Employee实例时会报编译错误呢?首先Manager和CEO都为<? super Manager>的子类,当他们被填充进去时就回被强制转为Manager的任意父类,而Employee不一定是<? super Manager>的子类, 所以不能会报错。

5 通配符总结

总结 ? extends 和 the ? super 通配符的特征,我们可以得出以下结论:
◆ 如果你想从一个数据类型里获取数据,使用 ? extends 通配符(能取不能存)
◆ 如果你想把对象写入一个数据结构里,使用 ? super 通配符(能存不能取)
◆ 如果你既想存,又想取,那就别用通配符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值