• 泛型(2)-深入泛型


      所谓泛型,就是允许在定义类,接口,方法时使用类型形参,这个类型形参(或叫泛型)将在声明变量,创建对象,调用方法时动态地指定(也就是传入实际的类型参数,也可称为类型实参).Java5改写了集合框架中的全部接口和类,为这些接口和类增加了泛型支持从而可以在声明集合变量,创建集合对象时传入类型实参,就是List<String>,ArrayList<String>两种类型.

      下面是Java5改写List接口,Iterator接口,Map的代码片段

    package com.j1803.genericTest;

    import javafx.scene.effect.SepiaTone;

    import java.util.Iterator;

    public interface ListTest <E>{
    //在该接口中,E可作为参数类型使用
    //下面方法可以使用E作为参数类型
    void add(E e);
    Iterator<E> interator();
    }
    interface Iterator <E>{
    E next();
    boolean hashNext();
    ...
    }
    //定义在接口中使用两个泛型参数,其形参名为K,V
    interface MapTest<K,V>{
    set<k> keyset();
    v put(k key,v value);
    }

    上面三个接口声明都是比较简单的,除了尖括号中的内容,这就是泛型的实质:允许在定义接口,类时声明泛型形参,泛型形参在整个接口,类中都可以当成类型使用,几乎所有可使用普通类型的地方都可以使用这种泛型形参.
    返回值类型时Iterator<E>,Set<E>,这表明Set<E>是一种特殊的数据类型,是一种与Set不同的的数据类型,可以认为是Set类型的子类.
    使用List类型时,如果为E形参传入String类型实参,则产生了一个新的类型:List<String>类型,可以把List<String>想象成E全部被String替换的特殊的List子接口
    等同于:
    //List<String>等同于如下接口
    public interface ListString extends List{
    //原来的E形参全部变成了String类型实参
    void add(String x)
    Iterator<String> iterator()
    }
    包含有泛型声明的类型可以在定义变量,创建对象时传入一个类型实参,从而可以动态的生成无数个逻辑上的子类,但这种子类在物理上并不存在.
    可以为任何类,接口增加泛型声明(并不仅仅包括集合类才可以使用泛型声明),代码示例
    package com.j1803.genericTest;

    public class AppleTest <E>{
    //使用E类型定义实例变量
    private E info;
    //定义构造方法
    public AppleTest() {

    }
    public AppleTest(E info){
    this.info=info;
    }

    public E getInfo() {
    return info;
    }

    public void setInfo(E info) {
    this.info = info;
    }

    public static void main(String[] args) {
    AppleTest test1=new AppleTest();
    test1.setInfo("红的");
    System.out.println(test1.getInfo());
    AppleTest<String> test2=new AppleTest<>("绿的");
    System.out.println(test2.getInfo());
    AppleTest<Double> test3=new AppleTest<>(56.12);
    System.out.println(test3.getInfo());
    }
    }

    当创建带泛型声明的自定义类,为该类定义构造器时,构造器还是原来的类名,不要增加泛型声明.如public Apple(){},调用该构造器时却可以使用Apple<E>的形式,当然应该为T形参传入实际的类型参数.
    =======================================================================================================================================================
    从泛型类派生子类
    当创建了带有泛型声明的接口,父类之后,可以为接口创建实现类,或从该父类中派生子类,需要指出的是,当使用这些接口,父类时不能再包含泛型形参.例如
    //定义类A继承AppleTest类,Apple类就不能跟泛型形参
    public class A extends AppleTest<E>{}//直接提示编译错误
    方法中的形参代表变量,常量,表达式等数据,称为形参,再调用方法时必须为这些数据形参传入实际的数据;类似的,定义类,接口,方法时可以声明泛型形参,使用类,接口,方法时应该为泛型形参传入实际的类型,例如,上面的可以改成
    pulic class A extends AppleTest<String>.
    示例代码
    package com.j1803.genericTest;
    public class Apple extends AppleTest<String>{
    public Apple() {
    super("78.56");
    }

    public void show(){
    System.out.println(super.getInfo());
    }

    public static void main(String[] args) {
    Apple apple=new Apple();
    apple.show();
    }
    }

    answer:
    78.56
    Process finished with exit code 0
    当然调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类,接口时也可以不为泛型形参传入实际的类型参数,即下面的代码也是正确的
    //使用Apple类时,没有为E形参传入实际的类型参数
    public class A extends AppleTest
    像使用AppleTest类时省略泛型的形式被称为原始类型(raw type).
    注意如果从AppleTest<String>类派生子类,则AppleTest类所使用E类型的地方都被替换成String类,在子类重写父类的方法时,必须要注意到这一点,以防出错.
    如果使用AppleTest时没有传入实际的类型(即使用原始类型),Java编译器可能发出警告,
    package com.j1803.genericTest;

    public class Apple2 extends AppleTest{


    //子类重写父类的方法,注意自动生成的的类型为Object类型,也就是如果AppleTest如果使用AppleTest时不加实际的类型,则默认为Object类型

    @Override
    public Object getInfo() {
    return super.getInfo();
    }

    @Override
    public void setInfo(Object info) {
    super.setInfo(info);
    }

    public Apple2(Object info) {
    super(info);
    }

    public static void main(String[] args) {
    Apple2 apple2=new Apple2("红色");
    System.out.println(apple2.getInfo());
    }
    }
    class Apple3 extends AppleTest<String>{
    @Override
    public String getInfo() {
    return super.getInfo();
    }

    @Override
    public void setInfo(String info) {
    super.setInfo(info);
    }

    public Apple3(String info) {
    super(info);
    }
    public static void main(String[] args) {
    Apple3 apple3=new Apple3("绿色");
    System.out.println(apple3.getInfo());
    }
    }

    上面程序都是从带泛型声明的父类派生子类,创建带泛型声明的接口的实现类几乎与此完全一样,此处不再赘述.
    package com.j1803.genericTest;

    public interface Fruit <E>{

    public abstract E getinfo();
    public abstract void setInfo(E e);
    }

    class Banana implements Fruit <String>{
    String msg;

    public Banana(String msg) {
    this.msg = msg;
    }

    @Override
    public String getinfo() {
    return this.msg;
    }

    @Override
    public void setInfo(String s) {
    this.msg=s;

    }

    public static void main(String[] args) {
    Banana banan=new Banana("黄色");
    System.out.println(banan.getinfo());
    banan.setInfo("绿色");
    System.out.println(banan.getinfo());

    }
    }
    answer:
    黄色
    绿色
    ==============================================================================================================================================================================
    并不存在的泛型类
      前面提到可以把ArrayList<String>类当成ArrayList的子类,事实上,ArrayList<String>类也确实像一种特殊的ArrayList类,该ArrayList<String>对象只能添加String对象作为集合元素,但实际上,系统并没有为ArrayList<String>生成新的
    class文件,而且也不会把ArrayList<String>当成新类来处理.
    package com.j1803.genericTest;
    import java.util.ArrayList;
    import java.util.List;

    public class EqualsListTest {
    public static void main(String[] args) {
    //分别创建List<String>List<Integer>对象
    List<String>list1=new ArrayList<>();
    List<Integer>list2=new ArrayList<>();
    System.out.println(list1.getClass()==list2.getClass());
    }
    }
    answer:
    true.
      上面应该输出false,可实际输出为true,因为不管泛型的实际类型参数是什么,它们在运行时总有相同的类class,因此不管为泛型参数传入哪一种类型实参,对于Java来说,它们仍然被当成同一个类处理,在内存中,也只占用一块内存资源,
    因此在静态方法,静态初始化块或者静态变量的声明和初始化总不允许使用泛型参数,
    package com.j1803.genericTest;
    public class Demo <T>{
    private static T no;//编译错误
    T age;
    public void foo(T msg){}
    public static show(T msg){}//编译错误
    }
    由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类,例如,下面代码时错误的
    java.util.Collection<String>cs=new java.util.ArrayList<>();
    if(cs instanceof java.util.Collection<String>)//编译错误
    {...}





      

  • 相关阅读:
    【leetcode】第一个只出现一次的字符
    【leetcode】0~n1中缺失的数字
    054696
    053695
    053694
    053693
    053692
    053691
    053690
    053689
  • 原文地址:https://www.cnblogs.com/shadow-shine/p/9638271.html
Copyright © 2020-2023  润新知