java---泛型(Generics)

泛型是 JDK1.5 以后增加的,它可以帮助我们建立类型安全的集合。

什么是泛型

泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入,可以把“泛型”理解为数据类型的一个占位符(类似:形式参数)

  1. 把类型当作是参数一样传递。、

  2. <数据类型> 只能是引用类型。

两个好处:

  代码可读性更好【不用强制转换】

  程序更加安全【只要编译时期没有警告,运行时期就不会出现 ClassCastException 异常】

 

编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。

泛型主要用于编译阶段,编译后生成的字节码 class 文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。

类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型

定义泛型

泛型标记  对应单词  说明
E  Element  在容器中使用,表示容器中的元素
 Type  表示普通的 JAVA 类
K  Key   表示键,例如:Map 中的键 Key
V  Value  表示值
 Number   表示数值类型
   表示不确定的 JAVA 类型

语法结构

普通java类泛型标记

public class 类名<泛型表示符号> {
}
public class GenericDemo1 {
    private static class Generic<T> {  // 定义一个泛型类 ,泛型类型为T,【根据实例化的时候定义详细引用类型
        private T flag; // T泛型类型待定

        public void setFlag(T flag) {
            this.flag = flag;
        }

        public T getFlag() {
            return flag;
        }
    }
    public static void main(String[] args) {
        Generic<String> generic = new Generic<>();  // 实例化泛型类,泛型为String类型,name这个类的所有 T 都将变成String
        generic.setFlag("admin");
        String flag = generic.getFlag();
        System.out.println(flag);

        Generic<Integer> generic1 = new Generic<>();// 实例化泛型类,泛型为String类型,name这个类的所有 T 都将变成integer【int的包装类】
        generic1.setFlag(100);
        Integer flag1 = generic1.getFlag();
        System.out.println(flag1);

    }

}

泛型接口

泛型接口泛型类 的声明方式一致。泛型接口的具体类型需要在实现类中进行声明。

public interface 接口名<泛型表示符号> {
}
public class interfaceimp implements 接口名<泛型具体类型>{
    @Override
    ······
}
public class GenericDemo2 {
    public interface Igeneric<T> {  // 定义一个 接口泛型
        T getName(T name);
    }

    public static class IgenericImpl implements Igeneric<String> {  // 实现接口类,并将泛型定义为String类型
        @Override
        public String getName(String name) {  // 实现接口类,重写里面的getName方法
            return name;
        }
        public int getAge(int a){
            return a;
        }
    }

    public static void main(String[] args) {
        IgenericImpl igeneric = new IgenericImpl();  // 直接实例化接口实现类
        String name = igeneric.getName("张三");
        System.out.println(name);
        Igeneric<String> igeneric1 = new IgenericImpl(); // 接口向上转型为接口Igeneric,调用getName,因为此方法已经被IgennericImp实现,所以可以调用
        String name1 = igeneric1.getName("kkkkk");
//        int age = igeneric1.getAge(14);   // 因为igeneric1 被认为是 Igeneric类型 ,而接口中没有getAge这个方法,所以不能调用
        int age1 = igeneric.getAge(16); // getAge是接口实现类里面的方法,所以可以调用
        System.out.println(name1);

    }

}

 

泛型方法是指将方法的参数类型定义成泛型,以便在调用时接收不同类型的参数。

类型参数可以有多个,用逗号隔开,如:<E,C,W>。定义时,类型参数一般放到返回值前面。

调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型来。--》这一点就像解释性语言不用写数据类型一样

泛型方法---非静态方法

public <泛型表示符号> void getName(【形参类型】泛型表示符号 【形参名字】name){  
    
}

public <泛型表示符号> 【返回类型】泛型表示符号 getName(【形参类型】泛型表示符号 【形参名字】name){
}
public class demo3_link {
        public <T> void setName(T name){  // 非静态方法--方法泛型
            System.out.println(name);
        }
        public <T> T getName(T name){
            return name;
        }
}


public class GenericDemo3 {
    public static void main(String[] args) {
        demo3_link methodGeneric = new demo3_link();  // 实例化非静态方法的类
        methodGeneric.setName("whoami");  // 调用非静态方法--泛型 泛型类型编译器自动判断出来 ====>String类型
        methodGeneric.setName(123123);  // ====>int类型
        demo3_link methodGeneric2 = new demo3_link();
        String name = methodGeneric2.getName("whereisme");
        Integer name1 = methodGeneric2.getName(321321);
        System.out.println(name1);
        System.out.println(name);

    }
}

泛型方法---静态方法

 静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。

public static <泛型表示符号> void setName(泛型表示符号name){
    
}
public static <泛型表示符号> 泛型表示符号 getName(泛型表示符号name){

}
public class GenericDemo4 {
    public static class MethodGeneric {
        public static <T> void setName(T name) {
            System.out.println(name);
        }

        public static <T> T getName(T name) {
            return name;
        }
    }
    public static void main(String[] args) {
//      类名.静态方法名的格式来调用静态方法 ,也是自动识别泛型具体类型
        MethodGeneric.setName("张三");
        MethodGeneric.setName(1010122);
//        实例化类,用对象名.方法名调用, 也是自动识别泛型具体类型
        MethodGeneric methodGeneric2 = new MethodGeneric();
        String name = methodGeneric2.getName("new张三");
        Integer name1 = methodGeneric2.getName(123);
        System.out.println(name1);
        System.out.println(name);
    }
}

泛型方法与可变参数

在泛型方法中,泛型也可以定义可变参数类型

  • 可变参数的注意事项

    • 这里的变量其实是一个数组

    • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后

public <泛型表示符号> void showMsg(泛型表示符号... agrs){ // ...表示可变
    
}
public class Metherd5_link {

    public <T> void method(T... args) {  // 遍历方法   ... 这三个点为可变
        for (T t : args) {
            System.out.println(t);
        }
    }
}

public class Metherd5 {
    public static void main(String[] args) {
        Metherd5_link methodGeneric = new Metherd5_link();
        String[] arr = new String[]{"a","b","c"};
        Integer[] arr2 = new Integer[]{1,2,3};
        methodGeneric.method(arr); // 遍历abc
        methodGeneric.method(arr2);// 遍历123
    }

}

通配符和上下限定

无界通配符

“?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。解决当具体类型不确定的问题

public void showFlag(Generic<?> generic){
}
public class Unbounded_wildcard {

    public static class Generic<T> {   // 定义一个一个泛型类
        private T flag;

        public void setFlag(T flag) {
            this.flag = flag;
        }

        public T getFlag() {
            return this.flag;
        }
    }

    public static class ShowMsg {
        public void showFlag(Generic<?> generic) {  // “?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。
            System.out.println(generic.getFlag());
        }
    }

    public static void main(String[] args) {
        ShowMsg showMsg = new ShowMsg();
        Generic<Integer> generic = new Generic<>();
        generic.setFlag(202222);  // integer
        showMsg.showFlag(generic);
        Generic<Number> generic1 = new Generic<>();
        generic1.setFlag(50);  // Numer
        showMsg.showFlag(generic1);
        Generic<String> generic2 = new Generic<>();
        generic2.setFlag("this is flag3");  // 字符串类型
        showMsg.showFlag(generic2);
    }
}

通配符的上限限定

上限限定表示通配符的类型是 T 类以及 T 类的子类或者 T 接口以及T 接口的子接口。该方式同样适用于与泛型的上限限定。

public void showFlag(Generic<? extends Number> generic){
}

 

通配符的下限限定

下限限定表示通配符的类型是 T 类以及 T 类的父类或者 T 接口以及T 接口的父接口。注意:该方法不适用泛型类。

public void showFlag(Generic<? super Integer> generic){
}

 

泛型总结

泛型主要用于编译阶段,编译后生成的字节码 class 文件不包含泛型中的类型信息。类型参数在编译后会被替换成 Object,运行时虚拟机并不知道泛型

1. 基本类型不能用于泛型。

  Test<int> t; 这样写法是错误,我们可以使用对应的包装类;Test<Integer> t ;

2. 不能通过类型参数创建对象。
  T elm = new T();  运行时类型参数 T 会被替换成 Object,无法创建T 类型的对象,容易引起误解,所以在 Java 中不支持这种写法。

 

posted @ 2022-10-22 15:58  link-零  阅读(62)  评论(0编辑  收藏  举报