• Java 泛型初识


    1. 什么是泛型?
    • 一个泛型类(generic class)就是有一个或多个类型变量的类。
    • 又叫做参数化类型,将类型当做参数传递给一个类或者方法
    1. 为什么要使用泛型程序设计?
    • 泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用。泛型可以看作是普通的工厂
    1. 泛型类声明
    原生态类型
    /**
    * 原生态类型
    */
    public class Stack {
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        public Stack() {
            elements = new Object[DEFAULT_INITIAL_CAPACITY];
        }
    
        public void push(Object e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public Object pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
            Object result = elements[size--];
            elements[size] = null;
            return result;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, 2 * size + 1);
            }
        }
    }
    

    泛型类声明方式一

    /**
    * 原生态类型转泛型类型方式一(较常用)
    */
    public class StackE1<E> {
        private E[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        @SuppressWarnings("unchecked")
        public StackE1() {
            // 创建Object 数组,并转换成泛型数组
            elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
        }
    
        public void push(E e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public E pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
            E result = elements[size--];
            elements[size] = null;
            return result;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, 2 * size + 1);
            }
        }
    }
    

    泛型类声明二

    /**
    * 原生态类型转泛型类型方式二
    */
    public class StackE2<E> {
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        public StackE2() {
            // 创建Object 数组,并转换成泛型数组
            elements = new Object[DEFAULT_INITIAL_CAPACITY];
        }
    
        public void push(E e) {
            ensureCapacity();
            elements[size++] = e;
        }
    
        public E pop() {
            if (size == 0) {
                throw new EmptyStackException();
            }
            @SuppressWarnings("unchecked")
            E result = (E) elements[--size];
            elements[size] = null;
            return result;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
    
        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, 2 * size + 1);
            }
        }
    }
    
    1. 泛型方法
    原始泛型方法
    public class StackMethod {
        public static Set union(Set s1,Set s2){
            Set result = new HashSet(s1);
            result.add(s2);
            return result;
        }
    }
    

    泛型方法声明

    public class StackMethodM1 {
        // 1.public static Set<E> 之间的<E> 代表什么意思?
        // 它表示一个类型参数,能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符,这个过程称为类型推导
        // 2.Set<E> s1, Set<E> s2 代表什么意思?
        // 字母E是一个代号,被称为类型变量或类型参数,当然你也可以换成a,b,c...这么写更规范,通常有E(element)、T(type)、K、V、N(number)
        // 注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char...)
        public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
            Set<E> result = new HashSet<E>(s1);
            result.addAll(s2);
            return result;
        }
    }
    
    1. 泛型单例工厂(类型推导的应用)
    泛型静态工厂方法,目的是为了消除,Map<String,List<String>> map = new HashMap<String,List<String>>(),标记部分的冗余部分
    public class HashMapUtil {
        // 泛型静态工厂方法
        public static <K, V> HashMap<K, V> newInstance() {
            return new HashMap<K, V>();
        }
    }
    

    客户端实例

    public class HashMapInstance {
        public static void main(String[] args) {
            // 利用工具类创建HashMap 对象
            Map<String, List<String>> map = HashMapUtil.newHashMap();
            // 原始方法创建HashMap 对象
            Map<String, List<String>> map1 = new HashMap<String, List<String>>();
        }
    }
    
    1. 递归类型限制
    定义一个泛型接口,跟泛型类差不多
    public interface IUnaryFun<T> {
        T apply(T arg);
    }
    

    假设需要提供一个恒等函数,如果在每次需要的时候都重新创建一个,这样会很浪费,因为他是无状态的,如果泛型被具体化了,每个类型都需要一个恒等函数,但是他们被擦除以后,就只需要一个泛型单例。

    /**
    * 递归限制类型
    */
    public class RecursiveType {
        // 泛型单例工厂模式
        private static IUnaryFun<Object> IDENTITY_FUN = new IUnaryFun<Object>() {
            public Object apply(Object arg) {
                return arg;
            }
        };
    
        // 无状态的参数类型
        @SuppressWarnings({ "unchecked" })
        private static <T> IUnaryFun<T> identityFun() {
            return (IUnaryFun<T>) IDENTITY_FUN;
        }
    
        public static void main(String[] args) {
            String[] strings = { "bob", "lily", "lucy" };
            IUnaryFun<String> sameString = identityFun();
            for (String s : strings) {
                System.out.println(sameString.apply(s));
            }
    
            Number[] numbers = { 1, 1.1d, 2.2f };
            IUnaryFun<Number> sameNumber = identityFun();
            for (Number n : numbers) {
                System.out.println(sameNumber.apply(n));
            }
        }
    }
    1. 通配符限制和PECS
    在泛型类型声明一种添加的pushAll() 方法,如下
    public void pushAll(Iterable<E> src) {
            for (E e : src) {
                push(e);
            }
        }

    客户端如下图的错误信息,因为如前所述,参数化类型是不可变得,因此无法将Integer 类型的数据插入Number 类型的参数中,尽管Number 是Integer 的父类

    Java 提供一种特许的参数化类型:有限制的通配符类型 来处理这种情况。pushAll 输入的参数不应为“E 的Iterable 接口”,而应该为“E 的某个子类型的Iterable 接口”,有一个通配符类型正符合此意:Iterable<? Extends E>,修改后的代码如下,StackE3 可以正确无误地编译,这个也叫上界通配符
    public void pushAll(Iterable<? extends E> src) {
            for (E e : src) {
                push(e);
            }
        }
    

    编写与pushAll 方法呼应的popAll方法,从堆栈中弹出每个元素,并将这些元素添加到指定集合中,在泛型类型声明一种添加的popAll() 方法,如下

    public void popAll(Collection<E> dst) {
            while (!isEmpty()) {
                dst.add(pop());
            }
        }
    

    客户端如下图错误信息,同样的,因为Collection<Object> 不是Collection<Number> 的子类型

    popAll 的输入参数类型不应该为“E 的集合”,而应该为“E 的某种超类的集合”,通俗的讲,就是Number 类型的数据无法添加到Object 类型,他们之间一开始是没关系的,这时仍有一个通配符正符合此意:Collection<? super E>。这也叫下界通配符
    public void popAll(Collection<? super E> dst) {
            while (!isEmpty()) {
                dst.add(pop());
            }
        }
    

      

    声明:本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接。 
  • 相关阅读:
    pdf.js-----后端返回utf-8数据流,前端处理数据展示pdf
    正则表达式之去除前后空格
    ng之邮箱校验
    ng-校验重复并提示具体重复内容
    input 数值框处理
    逻辑之不重复
    ng -----监听变化($scope.$watch())
    js中document的用法
    php中12个魔术方法
    php 中const和 define的区别
  • 原文地址:https://www.cnblogs.com/hellovoyager1/p/9202096.html
Copyright © 2020-2023  润新知