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类

    可变参数与泛型方法

  • 相关阅读:
    Java
    Java
    Java
    Java
    Java
    Hang Gliding线段树
    Biggest Number深搜
    2021年暑假康复性训练(Codeforces Round #731 (Div. 3))全题解
    Python GUI tkinter 随机生成题目
    ModuleNotFoundError: No module named ‘exceptions‘ 情况解决
  • 原文地址:https://www.cnblogs.com/rookiechen/p/5561197.html
Copyright © 2011-2022 走看看