泛型

泛型

1.什么是泛型?

Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法
        

ArrayList<String> list = new ArrayList<>();
        list.add("呵呵");
// 里面的<String>就是"泛型"
//作用:可以约束这个集合中只能存储某一种固定的类型,能够保证集合使用的安全。


    2.定义泛型类:

public class MyList<E> {//指定Mylist集合存储的数据类型是<E>类型
     public void add(E e) {
     }
     public E get(){
        return null;
      }
}


    3.定义具有"泛型"的方法:
      

 public class Demo04 {

   public static void main(String[] args) {
       ArrayList<String> put = Demo04.<String>put("10", "呵呵", "10", "呵呵", "10", "呵呵", "10", "呵呵", "10", "呵呵");
        ArrayList<Integer> list3 = Demo04.<Integer>put(10, 11, 12, 13, 14, 15, 16, 17, 18, 19);
            }

            //定义一个数据类型是<T>类型的方法,可以接收10个数据,然后封装到一个集合,并返回集合
  public static <T> ArrayList put(T s1, T s2, T s3, T s4, T s5,
                                            T s6, T s7, T s8, T s9, T s10) {
                ArrayList<T> strList = new ArrayList<>();
                strList.add(s1);
                strList.add(s2);
                strList.add(s3);
                strList.add(s4);
                strList.add(s5);
                strList.add(s6);
                strList.add(s7);
                strList.add(s8);
                strList.add(s9);
                strList.add(s10);

                return strList;
            }

        }

    4.定义具有"泛型"的接口:跟泛型类一样。
        

定义父类接口:

public interface Animal<E> {//定义父类接口时,只需在类名后申明数据类型时<E>类型就行
            public void eat(E e);
        }    

     定义父类的继承子类时,子类有三种选择:

   1)第一种情况:子类可以丢弃泛型

//第一种情况:子类可以丢弃泛型
        class Z1 implements Animal {
            @Override
            public void eat(Object o) {
            }
        }


     2)第二种情况:子类可以指定为某种固定类型 

//第二种情况:子类可以指定为某种固定类型
        class Z2 implements Animal<String>{
            @Override
            public void eat(String s) {
                
            }
        }

    3)第三种情况:子类可以继续继承泛型     

  //第三种情况:子类可以继续继承泛型
        class Z3<E> implements Animal<E>{
            @Override
            public void eat(E e) {
                
            }
        }


    5).泛型通配符:

我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:

  • ?表示不确定的 java 类型

  • T (type) 表示具体的一个java类型

  • K V (key value) 分别代表java键值中的Key Value

  • E (element) 代表Element

? 无界通配符

引入无界通配符主要是为了程序的拓展性,比如:

我有一个父类 Animal 和几个子类,如狗、猫等,现在我需要一个动物的列表,我的第一个想法是像这样的:

List<Animal> listAnimals

但是在实际的开发中如果引入通配符的话,就可以通过类与类的继承关系来实现代码的复用。

List<? extends Animal> listAnimals

为什么要使用通配符而不是简单的泛型呢?通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。

static int countLegs (List<? extends Animal > animals ) {
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}

static int countLegs1 (List< Animal > animals ){
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}

public static void main(String[] args) {
    List<Dog> dogs = new ArrayList<>();
     // 不会报错
    countLegs( dogs );
    // 报错
    countLegs1(dogs);
}


        1).<? extends E>

extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。

在类型参数中使用 extends 表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

  • 如果传入的类型不是 E 或者 E 的子类,编译不成功

  • 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用

static int countLegs (List<? extends Animal > animals ) {
//定义的类的数据类型必须是animal及animal的继承子类
    int retVal = 0;
    for ( Animal animal : animals )
    {
        retVal += animal.countLegs();
    }
    return retVal;
}


        2).<? super E>:

用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。

private <T> void test(List<? super T> dst, List<T> src){
    for (T t : src) {
        dst.add(t);
    }
}

public static void main(String[] args) {
    List<Dog> dogs = new ArrayList<>();
    List<Animal> animals = new ArrayList<>();
    new Test3().test(animals,dogs);
}
// Dog 是 Animal 的子类
class Dog extends Animal {

}

dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。

上界通配符主要用于读数据,下界通配符主要用于写数据。


        3).<?>:

表示可以是任何类型。

4)?和 T 的区别

 

?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种 :

// 可以
T t = operate();

// 不可以
?car = operate();

简单总结下:

T 是一个 确定的类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

区别1:通过T来确保泛型参数的一致性

// 通过 T 来 确保 泛型参数的一致性
public <T extends Number> void
test(List<T> dest, List<T> src)

//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型
public void
test(List<? extends Number> dest, List<? extends Number> src)

 

区别2:类型参数可以多重限定而通配符不行

 

使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。

区别3:通配符可以使用超类限定而类型参数不行

类型参数 T 只具有 一种 类型限定方式:

T extends A

但是通配符 ? 可以进行 两种限定:

? extends A
? super A
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

成神之路.java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值