JAVA泛型 基本概念学习

泛型入门

增加泛型这一功能很大程度上都是为了让集合能记住元素的数据类型。在没有泛型之前,一旦把一个对象放进JAVA集合中,集合就会忘记对象的类型,并当成Object处理,取出时需要强制转换。

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型中T和?的区别:

https://blog.csdn.net/woshizisezise/article/details/79374460?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242

在定义泛型接口、类时,通过在类名或者接口名后面接尖括号使用类型形参。extends 后面接的类型代表该类型形参能定义的最高级别的父类;

public class a {

    public static void main(String args[]){

        Foo l = new Foo(){
            @Override
            public void Test(Number t) {

            }

            @Override
            public Number s() {
                return null;
            }
        };
    }
}

interface Foo<T extends Number>{
    void Test(T t);
    T s();
}

定义后,在接口内部或类内部,均使用T作为某一种统一的类型,用法跟普通类型一致;

注意:如果父类或者接口中有泛型形参,那么其子类就不能再接着带有泛型形参,而要为泛型形参指定明确的类型,或者不为其传入实际的类型参数;如果不为其传入实际类型参数,有关T的重写方法都得使用T最高级别的父类(默认为Object);

interface Foo<T extends Number>{
    void Test(T t);
}

class A implements Foo{
    @Override
    public void Test(Number number) {
        
    }
}
class B implements Foo<Integer>{
    @Override
    public void Test(Integer integer) {
        
    }
}

实际上,拥有泛型的类不管为其指定哪种泛型,其类结构都是相同的,例如List和List本质上是一样的类,如下代码c的值为true;

List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
boolean c = a.getClass()==b.getClass();
System.out.println(c);

类型通配符

对于一个泛型类,不同泛型对象不能混用,例如List对象不能被当成List对象使用,这一点和数组不同,倘若B是A的子类型,那么B[]也是A[]的子类型,但是List不是List的子类型,B[]自动向上转型为A[]的过程成为型变,数组支持型变,集合不支持;

为了表示各种泛型List的父类,可使用类型通配符(?),List<?>代表它的元素类型可以匹配任何类型。

public void test1(List<?> l){
    for(int i = 0;i< l.size();i++){
        System.out.println(l.get(i));
    }
}
    public void test2(List<Object> l){
        for(int i = 0;i< l.size();i++){
            System.out.println(l.get(i));
        }
    }
    
    
List<String> a = new ArrayList<>();
test1(a);//正常
test2(a);//报错

同样的,?可以用extends来制定其最高级别的父类,即传入的类型必须是上限的子类型;用super指定其下限,即传入的类型必须是下限的父类型;

泛型方法

public void sa(Collection<?> c,Object object){
    c.add(object);
}

对于上面的代码会报错,因为JAVA不允许把对象放进一个未知类型的集合,此时可以使用泛型形参;

public <T> void sa(Collection<T> c,T object){
    c.add(object);
}

一般来说JAVA会自主判断T是什么类型,当系统报错时,一般就是无法判断T类型,一定是代码传参出了问题,例如:

List<Integer> b = new ArrayList<>();
sa(b,"a");

函数内部会试图将String类型对象放进Integer类型的集合中,系统无法判断T到底指的哪种类型才能实现这一过程,因此报错;可使用下面两种方法;

public <E ,T extends E> void test1(Collection<E> c,T object){
    c.add(object);
}

public <T> void test(Collection<? extends T> from , Collection<T> to){
    for (T i:from) {
        to.add(i);
    }
}

泛型方法和类型通配符的区别

大多数时候都能用泛型方法来代替类型通配符。

public <T> void test(Collection<? extends T> from , Collection<T> to){
    for (T i:from) {
        to.add(i);
    }
}
public <T,E extends T> void test1(Collection<E> from , Collection<T> to){
    for (T i:from) {
        to.add(i);
    }
}
public <T> void test2(Collection<T> from , Collection<? super T> to){
    for (T i:from) {
        to.add(i);
    }
}

泛型方法被用来表示方法的一个或者多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系,如果没有这种关系,就不应该使用泛型方法;

如:一个方法中形参A依赖形参B,那么B就不应该使用类型通配符,因为如果B的类型不能确定,那么程序也无法定义A的类型,这时候只能用泛型方法;

泛型构造器

泛型可以放构造函数里面使用,这时可以在代码里面指定泛型到底是哪种类型

class B{

    public <T> B(T t){
        System.out.println(t.toString());
    }
}

//Main中的如下代码:
        new B("a");
        new <String> B("a");
        //下面会报错,因为指定了泛型是Integer,又传了一个String参数进去
        new <Integer> B("a");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值