浅谈泛型。

目录

一、泛型语法

1.1 语法

二、泛型类的使用

2.1 语法

三、泛型的上界

3.1 语法

四、泛型方法

4.1 泛型方法的定义

4.2 示例

五、通配符 

5.1 认识通配符

5.2 通配符的上界

 5.3 通配符的下界


一、泛型语法

如果我们想实现一个可以存放任何类型的数组,我们该如何设计呢?这时,我们就需要引出泛型来进行操作。

1.1 语法

泛型的语法如下:

class 泛型类名称<类型形参列表> {
 // 这里可以使用类型参数 
}
class ClassName<T1, T2, ..., Tn> {

 }
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ { 
// 这里可以使用类型参数 
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> { 
// 可以只使用部分类型参数 
}

接下来,我们来设计一个可以存放任何类型的数组。

class Array<T> {
   public T[] t = (T[]) new Object[10];

    public T getPos(int pos) {
        return this.t[pos];
    }
    public void setT(int pos,T t) {
        this.t[pos] = t;
    }

    @Override
    public String toString() {
        return "Array{" +
                "t=" + Arrays.toString(t) +
                '}';
    }
}

public class Test {
    public static void main(String[] args) {
        Array<Integer> array = new Array<>();
        array.setT(1,3);
        array.setT(2,5);
        array.setT(0,2);
        System.out.println(array.getPos(0));
        System.out.println(array.toString());
    }
}

这就是利用泛型来设计的一个什么类型都可以存放的数组。如果我在<>里面输入的是Integer之后,该数组就只能输入int类型数据,其他类型的数据无法存放。

注意:1. <>里面只能使用包装类类型。 

           2. 类名后的<T>代表占位符,表示当前类是个泛型类。

           3. 编译器会检测当前类型是否符合要求。

二、泛型类的使用

2.1 语法

泛型类<类型实参> 变量名;// 定义一个泛型类引用 
new 泛型类<类型实参>(构造方法实参) // 实例化对象

例子

Array<Integer> array = new Array<Integer>();

还可以把后面<>中的参数省略,因为编译器会从前往后推导出类型实参。

三、泛型的上界

有的时候,我们需要限制泛型的范围,那么我们就需要通过类型边界来约束。

3.1 语法

class 类名<类型形参 extends 类型边界> {
    //............
}

接下来,我们来一个例子:

class Array<T extends Number> {
    int a;
}
public class Test {
    public static void main(String[] args) {
        Array<Number> array = new Array<>();
        Array<Integer> array = new Array<>();
        Array<String> array1 = new Array<String>(); //编译错误,String不是Number的子类
    }
}

这就表明,只接受Number的子类(或者Number)做为T的类型参数。

注意:没设置边界的时候,可以默认为是extends了Object类型。

还有一个例子:

class Student<T extends Comparable<T>>{

}

当我们这样定义一个类的时候,在实例化Student的时候,<>里必须填入实现Comparable接口的类型参数。

四、泛型方法

4.1 泛型方法的定义

方法限定符 <类型形参列表> 返回类型 方法名(参数列表){
 //.........
}

例如:
public static <T> void func(T t){
 //.........
}

注意:<>是在返回类型的前面。

4.2 示例

public static <T> void func(T t){
    System.out.println("T:"+t+" 泛型方法");
}

public static void main(String[] args) {
    Integer a = 10;
     func(a);
}

上面就是一个非常简单的泛型方法。在使用泛型方法的时候,我们需要将参数列表一一对应的写好。<>可以省略,因为编译器可以通过后面的参数来推导出T的类型。这就叫做类型推导

五、通配符 

5.1 认识通配符

我们先来看一个例子:

class Message<T>{
    public T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

    public static void func(Message<String> message){
        System.out.println(message.getT());
    }
  public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setT("啦啦啦");
        func(message);
    }

程序很简单,就是实例化一个Message对象,然后调用set方法赋值,接着调用func函数来输出数据。但是,当我们的泛型类型为Integer的时候,程序就有问题了。


        Message<Integer> message1 = new Message<>();
        message1.setT(10);
        func(message1);

类型不兼容异常,这是因为func函数中参数为Message<String> message,这样的话,func就只能接收泛型参数为String的参数,Integer当然就不行了。

如果要解决这个问题,我们就需要我们的通配符 ?了。

  public static void func(Message<?> message){
        System.out.println(message.getT());
    }
  public static void main(String[] args) {
        Message<String> message = new Message<>();
        message.setT("啦啦啦");
        func(message);

        Message<Integer> message1 = new Message<>();
        message1.setT(10);
        func(message1);
    }

 当我把func的类型参数改为Message<?> message,程序就不会报错了,并且可以正常运行。

注意:在func里面无法修改参数,因为你并不知道 ?到底是什么类型。

因此,通配符的主要作用就是可以接收所有的泛型类型,但是用户无法修改参数。

5.2 通配符的上界

在<?>的基础上,通配符还可以限定范围。

<? extends 类>:设置通配符的上限(上界)
<? super 类>:设置通配符的下限(下界)

现在,我们先将通配符的上界

例子如下:

class Message<T>{
    public T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
class Food{

}
class Fruit extends Food{

}
class Apple extends Fruit{

}
class Banana extends Food{

}
class RedApple extends Apple{

}

    public static void main(String[] args) {
        Message<Apple> message = new Message<>() ;
        message.setT(new Apple());
        func(message);

        Message<Fruit> message1 = new Message<>();
        message1.setT(new Fruit());
        func(message1);
  
        Message<Food> message2 = new Message<>();
        message2.setT(new Food());
        //func(message2);//报错
    }
    public static void func(Message<? extends Fruit> message){
        //message.setT(new Fruit());
        //message.setT(new Apple());报错
        Fruit fruit = message.getT();
        System.out.println(fruit);
    
    }

从上面的代码我们可以知道:

1.当<? extends Fruit>的时候,?就只能是Fruit本身或者其子类。其余的会报错。

2.在func里,只能读取数据,但是却不可以修改数据。这是因为?的类型是Fruit以及其子类,我们并不知道?传过来得是什么类型,假如传一个Fruit过来,但你并不知道它是Fruit,因此你并不知道设置/修改一个什么数据。

这个是<? extends Fruit>的关系图:

 

 5.3 通配符的下界

语法:

<? super 下界>
<? super Integer>//代表可以传入Integer或者Integer的子类

 例子如下:

class Message<T>{
    public T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
class Food{

}
class Fruit extends Food{

}
class Apple extends Fruit{

}
class Banana extends Food{

}
class RedApple extends Apple{

}

    public static void main(String[] args) {
        Message<Apple> message = new Message<>() ;
        message.setT(new Apple());
        //func(message);//报错

        Message<Fruit> message1 = new Message<>();
        message1.setT(new Fruit());
        func(message1);

        Message<Food> message2 = new Message<>();
        message2.setT(new Food());
        func(message2);
    }
    public static void func(Message<? super Fruit> message){
        message.setT(new Fruit());Fruit本身,可以修改
        message.setT(new Apple());Fruit的子类,可以修改
        //message.setT(new Food()); Fruit的父类,报错

        System.out.println(message.getT());//可以直接输出
        //Fruit fruit = message.getT(); 不能接收,这里无法确定是哪个父类
    }

由上述代码我们可以知道:

1.如果通配符设置了下限,那么就只能修改数据,而不可以获取数据。因为此时?传来的都是Fruit本身或者自己的父类,你无法知道用什么类型去接收数据。

2.当<? super Fruit>的时候,就只能接收Fruit本身或者其父类。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值