java泛型

泛型基础

泛型类定义一个基本的Animals类

public class Animals {
    private String object;
    public void set(String object){this.object=object;}
    public String get(){return object;}
}

上面的代码缺点是将类型定死为String类型,当想传入其他参数时,必须重写Animals类

接着我们用泛型重新顶一下 Animals类

public class Animals {
    private T object;
    public void set(T object){this.object=object;}
    public T  get(){return object;}
}

这样定义的Animals类可复用 我们可以将替换成任何我们需要的类型

Animals a = new Animals();
Animals b = new Animals();
Animals c = new Animals();

泛型方法

泛型方法则是在返回类型前面加个

public static class Util{
    public static  boolean compare(Pair p1,Pair p2){
        return p1.getKey().equals(p2.getKey())&&
                p1.getValue().equals(p2.getValue());
    }
}

public static class Pair{
    private k key;
    private v value;
    public Pair(k key,v value){
        this.key = key;
        this.value = value;
    }
    public void setKey(){this.key = key;}
    public void setValue(){this.value = value;}
    public k getKey(){return key;}
    public v getValue() {return value;}
}

接下来就可以调用泛型类

Pair p1 = new Pair(123,"abc");
Pair p2 = new Pair(456,"cba");
boolean same = Util.compare(p1,p2);

边界符

来看下面这个函数

public static  int countcompare(T[] array,T elem){
    Integer count = 0;
    for (T e:array)
        if (e > elem)
            ++ count;
    return count;
}

这个代码是错误的,因为除了short, int, double, long, float, byte, char等原始类型,其他的类并不一定能使用操作符>,所以会报错
要怎么修改呢?
使用边界符 Comparable

public static > int Test(T[] anArray,T elem){
    Integer count = 0;
    for (T e:anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

调用Test

    public static void main(String[] args){
        Integer[] arr = {1,2,3,4,5,6};;
        Integer t =  Test(arr,4);
        System.out.println(t);
        
    }

程序正常运行结果:2

通配符

通配符分为上界通配符和下界通配符

为上界通配符

为下界通配符

为什么要用通配符和边界?

我们来看个例子
我们有个frult类,他的派生类Apple类

class Fruit{}
class Apple extends Fruit{}

接下来我们定义一个Plate类,用来放一个泛型的“东西”,简单定义他的放和取

class plate{
    private T item;
    public plate(T t){item = t;}
    public void set(T t){item = t;}
    public T get(){return item;}
}

现在我们定义一个"水果盘子",看逻辑上是可以装苹果

Plate p = new Plate(new Apple())

但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”

error:incompatiable types:Palte cannot be converted to Plate

上界通配符

这里就要用到我们说的上界通配符
Plate
加上这句话的意思是:一个能放水果以及一切水果派生类的盘子,说人话是一个能放所有水果的盘子 ,plate p = new plate(new Apple());

Plate p=new Plate(new Apple());
接下来我们扩展下,再创建水果,肉类,食物的类

#level1
class Food{}

#level2
class Meat{}
class Fruit{}

#level3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork  extends Meat{}
class Beaf extends Meat{}

#level4
class green Apple extends Apple{}
class red Apple extends Apple{}
image.png

下界通配符

现在我们来讲下界通配符
Plate
这段代码的意思就是,一个能装水果和所有水果基类的盘子,这里要注意下,PlateFruit的基类,而不是Apple的基类,如下图,红色区域:

image.png

上下界通配符的副作用

这样使用通配符也是有坏处的,你如容器部分可能会失效,我们定义下Plate类,给他赋予两个简单的功能set()存和get()取

    class plate{
        private T item;
        public plate(T t){item = t;}
        public void set(T t){item = t;}
        public T get(){return item;}
    }
上界通配符只能取不能存

Plate 回事盘子里的set方法失效,但get()还是能用的
如下面的set()代码

Plate p=new Plate(new Apple());`

# 不能存入任何元素
p.set(new Fruit());        #error
p.set(new Apple());      #error

# 读取出来的东西只能存在Fruit或他的基类
Fruit newFruit1 = p.get()
Object newFruit3 = p.get();
Apple newFruit4  = p.get();     #error

原因是编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道。可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在看到后面用Plate;赋值以后,盘子里没有被标上有“苹果”。而是标上一个占位符,来表示捕获一个Fruit或Fruit的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入Apple或者Meat或者Fruit编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。
所以通配符;和类型参数;的区别就在于,对编译器来说,所有的T都代表同一种类型

下界通配符

下界不影响往里存,但往外取只能放在Object对象里
使用下界会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。

Plate p=new Plate(new Fruit());

//存入元素正常
p.set(new Fruit());
p.set(new Apple());

//读取出来的东西只能存放在Object类里。
Apple newFruit3=p.get();    //Error
Fruit newFruit1=p.get();    //Error
Object newFruit2=p.get();

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

PECS原则

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

  • 频繁往外读取内容的,适合用上界Extends。
  • 经常往里插入的,适合用下界Super。

类型擦除

在java 1.5前,java是没有泛型这一概念的,java为了能向下兼容在转入jvm之前,将泛型将泛型相关的擦除转为object,这种在java术语中叫类型擦除

你可能感兴趣的:(java泛型)