泛型

作者: ArcherZang | 来源:发表于2019-12-10 15:14 被阅读0次

    所谓泛型,就是变量类型的参数化。
    泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能。
    方法、类或者接口中变量的类型定义成参数形式,然后在调用或者实例化时传入具体类型。

    使用泛型时如果不提供参数类型,即泛型类没有参数化,系统只会警告,此时类型为Object。

            /**基础使用*/
            List<String> list = new ArrayList<String>();
    //        list.add(1);
            list.add("aaa");
            //JDK1.7 版本可以省略后面的String
            List<String> jdkList = new ArrayList<>();
            jdkList.add("aaa");
    //        jdkList.add(1);
    

    注意点:

    1. <>中放的必须是引用数据类型, 不能是基本数据类型如int, double;
    2. 前后的泛型必须一致. 如上面所示,而不能是List<String> list = new ArrayList<Integer>()
      因此在JDK1.7时就推出了一个新特性叫菱形泛型(The Diamond), 就是说后面的泛型可以省略直接写成<>
    3. 不能创建泛型数组
    4. 无法从静态上下文中引用非静态 类型变量 T,即静态方法无法引用类的泛型,只能用方法泛型
    使用泛型的好处
    1. 提高安全性: 将运行期的错误转换到编译期. 如果我们在对一个对象所赋的值不符合其泛型的规定, 就会编译报错。
    2. 省去强转的麻烦: 比如我们在使用List时, 如果我们不使用泛型
    泛型中的标记符

    E - Element (在集合中使用,因为集合中存放的是元素)
    **T **- Type(Java 类)
    **K **- Key(键)
    **V **- Value(值)
    **N **- Number(数值类型)
    ?- 表示不确定的java类型
    **S、U、V ** - 2nd、3rd、4th types

    Object跟这些标记符代表的java类型有啥区别呢?
    Object是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T、E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。且泛型使用上限时可以使用父类的方法,这点Object做不到。

        @Test
        public void testFan(){
            /**基础使用*/
            List<String> list = new ArrayList<String>();
    //        list.add(1);
            list.add("aaa");
            //JDK1.7 版本可以省略后面的<String>
            List<String> jdkList = new ArrayList<>();
            jdkList.add("aaa");
    //        jdkList.add(1);
    
            /**泛型类*/
            String spring = "spring";
            TestFan<String> testFan = new TestFan(spring);
            Integer i_testE = 2;
    
            /**泛型方法和泛型类没有任何关系*/
            testFan.testU(i_testE);
            testFan.testT(i_testE);
            testFan.testPrintCurrentT();
            testFan.testStaticT(i_testE);
    
            /**泛型上限
             * 可以多边界
             * <T extends A & B & C>
             * 但是多边界使用时其实只能继承一个父类,
             * 并且要将他写在第一个位置上. 其他的都是其实现的接口, 如图十九:
             * 不在第一个位置上就会编译出错。
             * */
            TestFan<Integer> testFanInteger = new TestFan(1);
            TestFan<Float> testFanFloat = new TestFan(0.1f);
            TestFan<Double> testFanDouble = new TestFan(0.24d);
            TestFan<String> testFanString = new TestFan("nihao");
            Number number= new Number() {
                @Override
                public int intValue() {
                    return 10;
                }
    
                @Override
                public long longValue() {
                    return 10;
                }
    
                @Override
                public float floatValue() {
                    return 10;
                }
    
                @Override
                public double doubleValue() {
                    return 10;
                }
    
                @NonNull
                @Override
                public String toString() {
                    return "number + 10";
                }
            };
            TestFan<Number> testFanNumber = new TestFan(number);
            testFan.testUpT(testFanInteger);
            testFan.testUpT(testFanFloat);
            testFan.testUpT(testFanDouble);
            testFan.testUpT(testFanNumber);
    
            //错误: 不兼容的类型: TestFan<String>无法转换为TestFan<? extends Number>
    //        testFan.testUpT(testFanString);
    
            /**泛型下限*/
            TestFan<Integer> testFanDownInteger = new TestFan(1);
            TestFan<Float> testFanDownFloat = new TestFan(0.1f);
            TestFan<Number> testFanDownNumber = new TestFan(number);
            testFan.testDownT(testFanDownInteger);
    
            //错误: 不兼容的类型: TestFan<Float>无法转换为TestFan<? super Integer>
    //        testFan.testDownT(testFanDownFloat);
            testFan.testDownT(testFanDownNumber);
    
            /**
             * 泛型接口
             */
            //1.实现时已处理
            InterfaceImplTow interfaceImplTow = new InterfaceImplTow();
            interfaceImplTow.testCall("wo yi shi xian FanXing");
            //2.实例化处理
            InterfaceImplOne<String> interfaceImplOne = new InterfaceImplOne();
            interfaceImplOne.testCall("shi li hua shi xian");
    
            /**泛型继承
             * 如果传入的变量类型是泛型的子类,可以传入
             * */
            TestFan<Number> testFanExtends = new TestFan<>(1);
            Integer integer =  1;
            Double aDouble = 2.1d;
            Float aFloat = 3.1f;
            testFanExtends.testClassFan(integer);
            testFanExtends.testClassFan(aDouble);
            testFanExtends.testClassFan(aFloat);
            //错误: 不兼容的类型: String无法转换为Number
    //        testFanExtends.setId("String");
    
            /**数组不能有泛型*/
            //错误: 创建泛型数组
    
            /**Person 和 PersonObject区别
             * 展示了用泛型比用Object的好处,
             * 可以使用父类的方法*/
         }
    
         public class TestFan<T> {
             private T t;
             public TestFan(T t) {
                 this.t = t;
             }
    
             //这里的T来自类的T
             public String testClassFan(T t) {
                 this.t = t;
                 return t.toString();
             }
    
             public <U> U testU(U e){
                 System.out.println(e.toString());
                 return e;
             }
    
             //这里是泛型方法的T与类中的T无任何关系
             public<T> T testT(T t1){
                 System.out.println(t1.toString());
                 return t1;
             }
    
             public void testPrintCurrentT(){
                 System.out.println(t.toString());
             }
    
             //错误: 无法从静态上下文中引用非静态 类型变量 T
    //         public static void testStaticT(T tStatic){
    //             System.out.println(tStatic.toString());
    //         }
    
             public static <F> void testStaticT(F tStatic){
                 System.out.println(tStatic.toString());
             }
    
             public void testUpT(TestFan<? extends Number> testFan){
                 testFan.testPrintCurrentT();
             }
    
             public void testDownT(TestFan<? super Integer> testFan){
                 testFan.testPrintCurrentT();
             }
         }
    
        public interface TestFanInterface<T> {
            void testCall(T t);
        }
    
    
        public class InterfaceImplOne<T> implements TestFanInterface<T> {
             @Override
             public void testCall(T t) {
                System.out.println(t.toString());
             }
        }
    
         public class InterfaceImplTow implements TestFanInterface<String> {
             @Override
             public void testCall(String s) {
                 System.out.println(s);
             }
         }
    
    //    public class Person<T extends String> {
    //        private T name;
    //
    //        public Person(T id) {
    //            this.name = id;
    //        }
    //
    //        public T getId() {
    //            return name;
    //        }
    //
    //        public void setId(T id) {
    //            this.name = id;
    //        }
    //
    //        public boolean isContainsString(String a){
    //            return name.contains(a);
    //        }
    //    }
    //
    //    public class PersonObject {
    //        private Object name;
    //        public PersonObject(Object id) {
    //            this.name = id;
    //        }
    //
    //        public Object getId() {
    //            return name;
    //        }
    //
    //        public void setId(Object id) {
    //            this.name = id;
    //        }
    //
    //        public boolean isContainsString(String a){
    //            return name.contains(a);
    //        }
    //    }
    
    ?和 上下边界问题

    通配符不能出现在类声明上,编译器会报错。
    单独的? 即表示 ?extends object。

    参考下面的代码会发现:
      <? extends Person> 上边界通配符,只能get不能Add,IN。
      <? super Person>下边界通配符,只能add不能get,OUT。
      IN 表示只读,不能存数据;OUT表示存数据,不能读。官网进行了in 和 out说明

    上下边界的出现是为了解决泛型无法逆变与协变的问题(关于逆变与协变问参考文章)。
      Number num = new Integer(1);
      ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch编译器报错

    上下边界使用场景:方法参数类型,因为通常使用变量使用我需要既能读也能存

            List<? super Student> listDown = new ArrayList<>();
            List<? extends Person> listUp;
    
            /**
             * 下界OUT
             */
    //        listDown.add(new Person("001"));
            listDown.add(new Student("002"));
            //编译器报错类型不一致
    //        Person personDown = listDown.get(0);
            //------------分界线-------------
            /**
             * 上界IN
             */
            ArrayList arrayList = new ArrayList();
            arrayList.add(new Person("003"));
            listUp = arrayList;
    //        listUp.add(new Person("001"));
    //        listUp.add(new Student("002"));
            Person personUp = listUp.get(0);
            System.out.println(personUp.getName());
    

    相关文章

      网友评论

          本文标题:泛型

          本文链接:https://www.haomeiwen.com/subject/nygbgctx.html