Java泛型基础(一)

作者: 程序熊大 | 来源:发表于2017-12-09 20:39 被阅读454次

本文首发于个人网站:Java中的泛型(一)

本文主要介绍Java泛型的基本知识,包括目的、泛型类的基本用法和场景应用场景。

目的

  1. 编写更加“泛化”的代码,编写可以应付多重类型的代码
  2. Java中的泛型,用于实现“参数化类型”的概念
  3. 创造可以放不同类型对象的容器类,通过编译器来保证类型的正确;
  4. 能够简单&安全得创建复杂的模型

泛型类

定义

利用Java开发的时候,肯定会有一个类持有另一个或几个类的情况,在编写一些比较基础的组件,例如缓存操作组件,这类组件的逻辑差不多,但是希望能够处理不同的类型。

在Java SE5之前,我们可以通过Object这个类型来实现,举个例子:

package org.java.learn.generics;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 15:45
 */
public class Holder2 {

    private Object a;

    public Holder2(Object a) {
        this.a = a;
    }

    public void setA(Object a) {
        this.a = a;
    }

    public Object getA() {
        return a;
    }

    public static void main(String[] args) {
        Holder2 h2 = new Holder2(new Automobile());
        //这里需要进行类型的强制转换
        Automobile automobile = (Automobile) h2.getA();

        h2.setA("Not a Automobile");
        String s = (String) h2.getA();

        h2.setA(1); //这里发生了自动装箱
        Integer x = (Integer) h2.getA();
    }
}

上面这段代码,确实满足了一个组件持有不同类型对象的需求,但是也有两个问题:(1)取出对象的时候,需要进行类型的强制转换;(2)同一个容器对象,可以随意存取不同类型的对象,有出错的风险。JavaSE5引入了“泛型”的概念,使得代码可以应用于多个类型,同时还能避免上述我说的两个问题,上面的代码,如果用Java泛型实现,则如下所示:

package org.java.learn.generics;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 15:48
 */
public class Holder3<T> {

    private T a;

    public Holder3(T a) {
        this.a = a;
    }

    public void setA(T a) {
        this.a = a;
    }

    public T getA() {
        return a;
    }

    public static void main(String[] args) {
        Holder3<Automobile> h3 = new Holder3<>(new Automobile());
        Automobile automobile = h3.getA(); //这里不需要强制转换类型

//        h3.setA("Not an Automobile");  Error
//        h3.setA(1);                    Error
    }
}

上面的Holder3是持有一个类型参数T,还可以持有三个相同类型(或不同类型的)类型参数,代码如下:

package org.java.learn.generics;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 15:51
 */
public class Holder4<T> {

    private T a;
    private T b;
    private T c;

    public Holder4(T a, T b, T c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public T getA() {
        return a;
    }

    public T getB() {
        return b;
    }

    public T getC() {
        return c;
    }

    public void setA(T a) {
        this.a = a;
    }

    public void setB(T b) {
        this.b = b;
    }

    public void setC(T c) {
        this.c = c;
    }

    public static void main(String[] args) {
        Holder4<Automobile> holder4 = new Holder4<>(new Automobile(),new Automobile(), new Automobile());

        Automobile a = holder4.getA();
        Automobile b = holder4.getB();
        Automobile c = holder4.getC();
    }
}

应用场景

元组

在实际开发中,常常会有“一次方法调用返回多个对象的需求”,我现在有时候会为每个返回值创建一个Class的写法,但是这块不太符合领域模型的思想——混淆了Entity和Object Value的概念。利用元组,可以实现Object Value的概念。

package org.java.learn.util;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 16:18
 */
public class TwoTuple<A, B> {
    public final A first;
    public final B second;

    public TwoTuple(A first, B second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public String toString() {
        //这里隐含表示了:元祖中的元素是有顺序的
        return "(" + first + ", " + second + ")";
    }
}

这个例子中的final关键字用的非常漂亮,有两个设计上的考虑

  • 访问安全,客户端可以读取first和second两个对象,但是无法修改它们;
  • 体现Object Value的含义,如果开发者需要一个不同元素的元组,必须重新构建一个;

利用继承机制,我们还可以实现元素更多的元组,下面的代码展示了三元组和四元组的实现。不过,从另一个角度考虑——如果一个方法调用需要返回四个以上元素的元组,是不是需要考虑下这个方法本身的设计是否合理了呢。

三元组

package org.java.learn.util;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 16:32
 */
public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {

    public final C third;

    public ThreeTuple(A first, B second, C third) {
        super(first, second);
        this.third = third;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ", " + third + ")";
    }
}

四元组

package org.java.learn.util;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 16:36
 */
public class FourTuple<A, B, C, D> extends ThreeTuple<A, B, C> {

    public final D forth;

    public FourTuple(A first, B second, C third, D forth) {
        super(first, second, third);
        this.forth = forth;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ", " + third + "," + forth + ")";
    }
}

堆栈

再看一个比元祖复杂一点的例子,这里利用自己实现的链表存储机制构建了一个下推堆栈。

package org.java.learn.util;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 16:40
 */
public class LinkedStack<T> {

    private static class Node<U> {
        U item;
        Node<U> next;

        public Node() {
            item = null;
            next = null;
        }

        public Node(U item, Node<U> next) {
            this.item = item;
            this.next = next;
        }

        /**
         * 是否到达堆栈底部
         *
         * @return
         */
        boolean end() {
            return item == null || next == null;
        }
    }

    /**
     * 末端哨兵
     */
    private Node<T> top = new Node<>();
    public void push(T item) {
        top = new Node<>(item, top);
    }
    public T pop() {
        T res = top.item;
        if (!top.end()) {
            top = top.next;
        }
        return res;
    }

    public static void main(String[] args) {
        LinkedStack<String> lss = new LinkedStack<>();
        for (String s: "Phasers on stun!".split(" ")) {
            lss.push(s);
        }
        String s;
        while ((s = lss.pop()) != null) {
            System.out.println(s);
        }
    }
}

书中的练习题5:移除Node类上的类型参数,并修改LinkedStack.java的代码,说明内部类可以访问外部类的类型参数,我自己试了下,代码如下:

package org.java.learn.util;

/**
 * 作用: User: duqi Date: 2017/11/19 Time: 16:40
 */
public class LinkedStack2<T> {

    /**
     * 这里不能使用静态内部类
     */
    private class Node {
        T item;
        Node next;

        public Node() {
            item = null;
            next = null;
        }

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }

        /**
         * 是否到达堆栈底部
         *
         * @return
         */
        boolean end() {
            return item == null || next == null;
        }
    }

    /**
     * 末端哨兵
     */
    private Node top = new Node();
    public void push(T item) {
        top = new Node(item, top);
    }
    public T pop() {
        T res = top.item;
        if (!top.end()) {
            top = top.next;
        }
        return res;
    }

    public static void main(String[] args) {
        LinkedStack<String> lss = new LinkedStack<>();
        for (String s: "Phasers on stun!".split(" ")) {
            lss.push(s);
        }
        String s;
        while ((s = lss.pop()) != null) {
            System.out.println(s);
        }
    }
}

代码显示,静态内部类,无法访问其外部类的类型参数,但是非静态内部类可以。

RandomList

书中还给出一个随机列表的例子——一个持有特定类型对象的列表,select()方法可以随机取出列表中的一个元素。这是一个抽奖demo,或者我们可以用它决定每天中午吃什么😜。

package org.java.learn.generics;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 作用: 持有特定类型对象的列表,select()方法可以随机取一个元素
 * User: duqi
 * Date: 2017/11/19
 * Time: 17:08
 */
public class RandomList<T> {

    private ArrayList<T> storage = new ArrayList<>();

    private Random random = new Random(47);

    public void add(T item) {
        storage.add(item);
    }
    public T select() {
        return storage.get(random.nextInt(storage.size()));
    }

    public static void main(String[] args) {
        RandomList<String> randomList = new RandomList<>();
        for (String s: "The queick brown for jumped over the lazy brown dog".split(" ")) {
            randomList.add(s);
        }
        for (int i = 0; i < 11; i++) {
            System.out.print(randomList.select() + " ");
        }
    }
}

总结

泛型的东西很多,我之前看《Java核心技术》学过一遍,最近为了给团队的同学做分享,准备再结合《Java编程思想》复习一遍,发现《Java编程思想》这本书写得非常有深度,需要思考、实践和回味,才能准确得get到作者想要表达的意思。

相关文章

  • Java 泛型

    导读 移动开发知识体系总章(Java基础、Android、Flutter) 为什么会有泛型? 泛型类 泛型方法 泛...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • Java基础之泛型

    Java基础之泛型 泛型基本介绍 简单介绍在jdk1.6之前,还没有使用到泛型,Java类型分为原始类型、复杂类型...

  • spring 泛型处理

    java 泛型基础 泛型类型:泛型类型是在类型上参数化的泛型类或接口 泛型使用场景编译时前类型检查。定义为 Col...

  • Java泛型基础

    Java泛型基础 1. 认识泛型 泛型是在JDK1.5之后增加的新功能. 泛型可以解决数据的安全性问题, 主要的原...

  • 一文带你认识Java泛型基础

    Java泛型基础 1. 认识泛型 泛型是在JDK1.5之后增加的新功能. 泛型可以解决数据的安全性问题, 主要的原...

  • 第二十八课:泛型

    泛型出现之前 泛型出现之后 Java深度历险(五)——Java泛型

  • Java基础(一)

    Java要点1 JAVA基础 1.对抽象、继承、多态的理解 2.泛型的作用及使用场景 1.Java泛型是JDK1....

  • 详解Java泛型之3——十分钟理解泛型擦除

    前面我们介绍了泛型以及通配符的基础概念,可以参考文章: 详解Java泛型之1——入门泛型必懂的知识点[https:...

  • Android知识总结

    一、java基础 Java基础-泛型[https://www.jianshu.com/p/b7d88eec37d9...

网友评论

  • zoterap:网站已经打不开了?
    程序熊大:@zhuyinbai 域名解析有点问题,已经处理掉了,感谢提醒

本文标题:Java泛型基础(一)

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