zoukankan      html  css  js  c++  java
  • Java泛型及实践

    代码及说明:

    /**
     * @author zsm
     * @date 2016年11月2日 下午11:23:30
     * @version 1.0
     * @parameter
     * @return
     */
    // JDK5开始支持泛型。泛型(类、接口、方法);泛型通配符、通配符上限、通配符下限
    class _16_Generic {
        // 泛型
        List<String> strListStand = new ArrayList<String>();
        // 泛型菱形语法(JDK7后初始化时泛型的具体类型可省略,自动判断)
        List<String> strList = new ArrayList<>();
    
        // 泛型类、接口
        public interface Fruit<T> {
            void add(T fruit);
        }
    
        public interface MyMap<K, V> {
            Set<K> keySet();
    
            V put(K key, V value);
        }
    
        public static class Apple<T> {
            private T info;
    
            public Apple() {// 不能再写成Apple<T>
    
            }
    
            public Apple(T info) {
                this.info = info;
            }
    
            public T getInfo() {
                return this.info;
            }
    
            // 静态方法、静态初始化块、静态变量声明和初始化中不能使用类的泛型形参,静态类或接口定义处则可以
            // static T name;//报错
            // public static void bar(T msg) {}
            // static {T name = "xiaoming";}
    
            // 静态方法不能使用类的类型参数,除非该静态方法是个泛型方法
            public static <E, F> void ttt(E name, F age) {
                ;
            }
        }
    
        // 实现泛型接口或继承泛型类时:若新类不是泛型类,则父类(接口)必须指定类型实参而不能仍是类型形参;若新类仍是泛型类则父类(接口)可以仍用类型形参
        public class A1 extends Apple<String> {// 不能再写成Apple<T>
            public String getInfo() {
                return super.info;
            }
        }
    
        public class A2<T> extends Apple<T> {
            public T getInfo() {
                return super.info;
            }
        }
    
        // 带泛型形参的类不会随着类型实参的不同而成为不同的类,接口亦然
        static void testClassEqual() {
            System.out.println((new Apple<String>()).getClass() == (new Apple<Integer>()).getClass());// true
            List<String> strList = new ArrayList<>();
            List<Integer> intList = new ArrayList<>();
            System.out.println(strList.getClass() == intList.getClass());// true
        }
    
        // 若Foo是Bar的子类,则Foo[]是Bar[]的子类,但List<Foo>却不是List<Bar>的子类。可以使用泛型通配符:?,
        public void wildcardTest1(List<Object> c) {// 调用时只能传入List<Object>参数,不可传入List<String>等参数,因为后者不是前者的子类
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        }
    
        public void wildcardTest2(List<?> c) {// 此时可传入List<String>等参数,但只可从c读不可往里写,因为c类型不定。即带类型通配符定义的对象只可读不可写
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
                // c.add(new Object());//编译错误,不可往c里写,因为c里元素类型未定
            }
        }
    
        // 类型通配符上限
        public abstract class Shape {
            public abstract void draw(MyCanvas c);
        }
    
        public class Circle extends Shape {
            @Override
            public void draw(MyCanvas c) {
                // TODO Auto-generated method stub
                System.out.println("draw a circle on " + c);
            }
        }
    
        public class Rectangle extends Shape {
            @Override
            public void draw(MyCanvas c) {
                // TODO Auto-generated method stub
                System.out.println("draw a rectangle on " + c);
            }
        }
    
        public class MyCanvas {
            public void drawAll0(List<?> shapes) {// 使用通配符时类型未定,所以访问每个元素只能用终极父类Object,这导致得进行麻烦的强制类型转换
                for (Object obj : shapes) {
                    Shape s = (Shape) obj;
                    s.draw(this);
                }
            }
    
            public void drawAll1(List<? extends Shape> shapes) {// 使用通配符上限,从而知道元素元素类型的终极父类是Shape,这样不用进行强转。至多可以有一个类上限、多个接口上限,类上限需放最前。
                for (Shape s : shapes) {
                    s.draw(this);
                }
            }
        }
    
        // 泛型方法:方法中所用类型形参不要求在类声明中先出现该类型形参;类型形参位于方法修饰符和返回值之间;与泛型类、接口不同的是,无须在调用泛型方法时显式指明实参类型
        public <T> void fromArrayToCollection(T[] a, Collection<T> c) {// a的实参可以是T[]的子类型
            for (T o : a) {
                c.add(o);
            }
        }
    
        public void test_fromArrayToCollection() {
            fromArrayToCollection((new Object[10]), new ArrayList<Object>());
            fromArrayToCollection((new Integer[10]), new ArrayList<Object>());
            fromArrayToCollection((new Integer[10]), new ArrayList<>());// 与泛型类、接口不同的是,无须在调用泛型方法前显式指明实参类型(指在方法名前),编译器自己确定
            fromArrayToCollection((new Integer[10]), new ArrayList<Number>());
            // fromArrayToCollection((new Integer[10]), new ArrayList<String>());//编译错误
        }
    
        // 泛型方法和类型通配符:方法参数间或返回值与参数间存在类型依赖关系(如子类)时采用泛型方法,否则类型参数只用一次,没有存在的必要,可以改用类型通配符。
        public <T, S extends T> void copy1(List<T> des, List<S> src) {// S仅用了一次且与其他参数间没有依赖关系,因此没有存在的必要,改为下面的方法。
            for (S s : src) {
                des.add(s);
            }
        }
    
        public <T> void copy2(List<T> des, List<? extends T> src) {// 去掉S,改为使用类型通配符上限。
            for (T t : src) {
                des.add(t);
            }
        }
    
        // 泛型方法 ———— 泛型构造器
        class Foo<E> {
            public <T> Foo(T t) {
                System.out.println(t);
            }
    
            public void add(E e) {
    
            }
        }
    
        public void test_genericConstructor() {
            new Foo("good");
            new Foo(1);
            new <String>Foo("good");// 显示指定构造方法类型形参的实际类型
            new <String>Foo<Integer>("good");// 又指定了类的类型形参的实际类型
            new Foo<>("good");// 菱形语法
            // new<String> Foo<>(1);// 如果显示指定了构造器类型形参的类型,则不可用菱形语法
    
            Foo<Integer> p1 = new <String>Foo("good");
            Foo<Integer> p2 = new <String>Foo<Integer>("good");
            Foo<Integer> p3 = new Foo<Integer>("good");
            // Foo<Integer> p4 = new<String> Foo<>("good");//如果显示指定了构造器类型形参的类型,则不可用菱形语法
        }
    
        // 类型通配符下限。
        // 返回最后一个元素,类型不可丢。上面的copy2方法如果要返回最后一个被复制的元素,则返回的会是des中元素的类型T,这样就丢失了src的类型即S。可以通过修改copy1解决,也可以通过通配符下限解决。
        public <T, S extends T> S copy3(List<T> des, List<S> src) {// S仅用了一次且与其他参数间没有依赖关系,因此没有存在的必要,改为下面的方法。
            int i = 0;
            for (i = 0; i < src.size(); i++) {
                des.add(src.get(i));
            }
            return src.get(i - 1);
        }
    
        public <T> T copy4(List<? super T> des, List<T> src) {// 去掉S,改为类型通配符下限。
            int i = 0;
            for (i = 0; i < src.size(); i++) {
                des.add(src.get(i));
            }
            return src.get(i - 1);
        }
    
        // 檫除与转换:带泛型声明的类间转换
        public static void test_Transform() {
            List list;// 不指定实际类型参数,为raw type,默认为上限类型,此为Object
            List<Integer> listInteger = new ArrayList<Integer>();
            listInteger.add(1);
            listInteger.add(2);
            list = listInteger;// List<Integer>对象赋给未指定类型的List,原始类型丢失,变为Object
    
            List<String> listStr = list;// 编译没问题,只有警告:"未经检查的转换"
            System.out.println(listStr.get(0));// 但访问里面元素,会发生运行时异常
        }
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
        }
    
    }
    View Code
  • 相关阅读:
    计数排序
    CSS3变形
    前端内存泄露问题
    复杂对象的深拷贝
    JavaScript基本数据类型——Symbol
    随机打乱数组
    唯一重复的数字
    src和href的区别
    iframe的缺点
    link和@import的区别
  • 原文地址:https://www.cnblogs.com/z-sm/p/6259859.html
Copyright © 2011-2022 走看看