zoukankan      html  css  js  c++  java
  • 泛型

    一、泛型产生的原因

    ①、最主要的目的就是创造容器。

    举例:只能持有一个对象的类

    public class Holder{
       private Auto mAuto;
      public Holder(Auto auto){
           mAuto = auto;
      }
    
      public Auto getAuto(){
        return mAuto;
      }
    }
    Holder

    这个类只能持有Auto类型的对象,而不能持有其他类型的对象。缺点:如果想持有其他类型的对象,就需要重新在创建一个。

    在没有泛型的解决方法:就是将Auto转换成Object,这样其他对象就可以使用了

    缺点:但是Object类说明,什么样的类都可进入容器,但是一般我们只需要存储一种类型,而不是各种各样的类型都可以放进容器中。

    所以:泛型的主要作用是,确定容器要持有什么对象,然后通过编译器保证类型的正确性。

    举例:

    public class SimpeHolder<T> {
        
        private T base;
        
        public void setBase(T base) {
            this.base = base;
        }
    
        public T getBase() {
            return base;
        }
        
        public static void main(String[]args){
            //说明如果不添加泛型,T就被当成Object
            SimpeHolder simple = new SimpeHolder();
            simple.setBase(new Mocha());
            Object object = simple.getBase();
            //添加泛型的使用
            SimpeHolder<Mocha> mocha = new SimpeHolder<Mocha>();
            mocha.setBase(new Mocha());
            Mocha mo = mocha.getBase();
        }
    }
    SimpleHolder

    小知识:①、静态类型无法使用泛型   ②、不指定泛型,T代表Object

    二、元组

    产生的原因:以此方法调用能够返回许多个对象。可是return只能返回一个对象

    解决方法:创建一个对象,持有想返回的多个对象。(这就是元组)

    特性:这个对象允许读取元素,但不能够允许添加或者修改元素。

    举例:

    public class TwoTuple<BasicClass,BasicClass1>{
        //① 为什么用public final 而不使用private 加上get()方法
        public final BasicClass base1;
        public final BasicClass1 base2;
        public TwoTuple(BasicClass a,BasicClass1 b){
            base1 = a;
            base2 = b;
        }
    }
    TwoTuple

    知识:①、根据功能出发,我们声明private然后提供get()方法,不如使用final,因为final提供了同样的安全性。相比而言优点更加简洁明了。

    注意:元组的顺序是根据泛型的顺序确定的。

    三、通用的堆栈类

    public class ListStack<T> {
        public static class Node<T>{
            public T item;
            public Node next;
            //结合isEnd() 初始化Node为null,然后当到达尾部的时候,就是到达初始化Node,如果为null则表示到达尾部。
            public Node(){
                this.item = null;
                this.next = null;
            }
            public Node(T item,Node next){
                this.item = item;
                this.next = next;
            }
            public boolean isEnd(){
                return item == null && next == null;
            }
        }
        //初始化一个Node,不传值,用来进行是否达到队列尾部的判断
        private Node<T> top = new Node();
        public void push(T item){
            //创建node,并放入顶部
            Node node = new Node(item,top);
            top = node;
        }
        //获取栈顶 
        public T pop(){
            if (!top.isEnd()){
                T item = top.item;
                top = top.next;
                return item;
            }
            return null;
        }
        //返回true代表成功,返回false代表失败
        public boolean remove(){
            if (!top.isEnd()){
                Node oldTop = top;
                top = top.next;
                oldTop.next = null;
                return true;
            }
            return false;
        }
        
        public static void main(String[]args){
            ListStack<BasicClass> stack = new ListStack();
            for(int i=0; i<3; ++i){
                BasicClass item = new BasicClass();
                stack.push(item);
            }
            for(int i=0; i<5; ++i){
                BasicClass data = stack.pop();
                if (data != null)
                System.out.println(data);
            }
        }
    }
    ListStack

    技巧:明白栈的主要方法:入栈和出栈  ②、明白结构体的作用  ③、判定是否到达栈底

    四、泛型接口(生成器、适配器)

    作用:专门负责生成新的对象。 

    与工厂方法的比较:工厂方法一般需要参数  但是  生成器不需要参数。

    举例:

    新建:多个元素 继承Coffee  比如:Macha,Cappuccio,Breve

    新建:生成器Generator<T>

    public interface Generator<T> {
        T next();
    }
    Generator

    利用生成器产生随机的Coffee类

    public class CoffeeGenerator implements Generator<Coffee>,Iterable<Coffee>{
        //当使用Iterator的时候,进行判定的
        private int size;
        
        private Class [] objects = {Breve.class,Cappuccino.class,Mocha.class};
        //用普通方法生成对象
        public CoffeeGenerator(){}
        //用构造器生成对象
        public CoffeeGenerator(int size){
            this.size = size;
        }
        //生成器
        @Override
        public Coffee next() {
            // TODO Auto-generated method stub
            try {
                return (Coffee)objects[new Random().nextInt(objects.length)].
                        newInstance();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
        //定制迭代器
        class CoffeeIterator implements Iterator<Coffee>{
            @Override
            public boolean hasNext() {
                size = size-1;
                return size>0;
            }
            @Override
            public Coffee next() {
                // TODO Auto-generated method stub
                return CoffeeGenerator.this.next();
            }
            
        }
        @Override
        public Iterator<Coffee> iterator() {
            // TODO Auto-generated method stub
            return new CoffeeIterator();
        }
        
        public static void main(String[]args){
            
            //未使用迭代器
            CoffeeGenerator coffee = new CoffeeGenerator();
            for(int i=0; i<5; ++i){
                System.out.println(coffee.next());
            }
        System.out.println("-------------------------------------");
            //使用迭代器
            for(Coffee cof:new CoffeeGenerator(6)){
                System.out.println(cof);
            }
        }
    }
    CoffeeGenerator

    这里用到了迭代器:如何创建迭代器(复习知识)  迭代器也是使用到了泛型,因为要迭代的容器肯定是某一类,而不可能是多种类

    让CoffeeGenerator继承 Iterable<>接口:只有声明为Iterable才能使用迭代器。

    之后创建Iterator<>迭代器,创建类,继承Iterator接口,并实现

    之后将新建的Iterator对象交给Iterable<>  

    foreach循环能够执行继承Iterable接口的类

    适配器

    假设现在有:sequence这个类,这个类的作用是随机创建一个数字
    举例:

    public class RandomSequence {
        private int data;
        private Random mRandom;
        public RandomSequence(){
            mRandom = new Random();
        }
        public Integer getData(){
            return mRandom.nextInt(100);
        }
        
        public static void main(String[]args){
            RandomSequence random = new RandomSequence();
            for (int i=0; i<50; ++i){
                System.out.println(random.getData());
            }
        }
    }
    RandomSequence

    现在想通过利用foreach循环来创建数字。

    但是foreach循环需要是Iterable接口才能使用。

    所以为能够使用foreach循环,需要一个接口能够相互转换将Sequence类转换成Iterator类(或者说小明充电,他是苹果机(Squence),现在只有一条android数据线,所以需要一个能够连接android线的苹果插口,能够连接充电宝(foreach))

    新建IteratorSequence类 继承Iterabe接口(苹果连接android插口)提供了转化的工具

    ublic class IteratorSquence extends RandomSequence implements Iterable<Integer> {
        private int mCount = 0;
        public IteratorSquence(int count){
            mCount = count;
        }
        
        class Squence implements Iterator<Integer>{
            @Override
            public boolean hasNext() {
                // TODO Auto-generated method stub
                return mCount>0;
            }
    
            @Override
            public Integer next() {
                mCount = mCount + 1;
                // TODO Auto-generated method stub
                return IteratorSquence.this.getData();
            }
            
        }
        public static void main(String[]args){
            for(int data : new IteratorSquence(18)){
                System.out.println(data);
            }
        }
        @Override
        public Iterator<Integer> iterator() {
            // TODO Auto-generated method stub
            return new Squence();
        }
    }
    IteratorSquence

    这就是适配器模式:将一个类,通过适配,能够被另一个类使用。

    泛型:Iterable接口使用了泛型,使其能够通用。

    五、泛型方法(之前谈论的都是在类上加上泛型)

    使用:在返回值前添加泛型参数

    举例:

    public class GenericMethods {
        //在返回值前面添加参数
        public <T> void f(T data){
            System.out.println(data.getClass().getSimpleName());
        }
    
        public static void main(String[] args){
            GenericMethods methods = new GenericMethods();
            methods.f("");
            methods.f(12);
        }
    }
    GenericMethods

    特性:参数推断(编译器会为我们找出具体值)

    参数推断的作用:

    现在我们有Map<String,List<String>> map = new Map<String,List<String>>();这样写的方式太长了

    所以利用参数推断:

    public class ConcludeElements {
        public static <T,V> Map<T,V> map(){
            return new HashMap<T,V>();
        }
        
        public static void main(String[]args){
            //map()的返回值类型由前面的类型参数确定   (编译器自动进行参数推断)
            Map<String,List<String>> map = ConcludeElements.map();
        
        }
    }
    ConcludeElements

    这样可以减少重复的泛型参数列表:

    这还可以用在强制转换上,如果有个方法返回的是Object类型,需要强制转换成其他类型的类的时候,就可以很好的使用到。

    比如说android的findViewById():

    public class ConcludeElements {
        public <VT extends View> VT getViewById(int data){
            return (VT)findViewById(data);
        }
        
        public static void main(String[]args){
            Button btn = getViewById(R.id.main_btn);
        }
    }
    AndroidConclude

    注意:但是类型参数推断只对赋值有效(因为推断是根据前面的参数决定的,只有赋值才有前面的参数列表)

    如果直接使用方法,则参数推断的就是Object类

    可变参数与泛型方法

  • 相关阅读:
    利用相关的Aware接口
    java 值传递和引用传递。
    权限控制框架Spring Security 和Shiro 的总结
    优秀代码养成
    Servlet 基础知识
    leetcode 501. Find Mode in Binary Search Tree
    leetcode 530. Minimum Absolute Difference in BST
    leetcode 543. Diameter of Binary Tree
    leetcode 551. Student Attendance Record I
    leetcode 563. Binary Tree Tilt
  • 原文地址:https://www.cnblogs.com/rookiechen/p/5561197.html
Copyright © 2011-2022 走看看