什么是泛型?泛型的基本原理与使用优势。

1. 什么是泛型?

泛型将接口的概念进一步延伸,“泛型”的字面意思就是广泛的类型。类、接口和方法代码可以应用于非常广泛的类型,代码与它们能够操作的数据类型不再绑定在一起,同一套代码可以用于多种数据类型,这样不仅可以复用代码,降低耦合性,而且还提高了代码的可读性以及安全性。讲起来优点抽象,我们看个实际的例子。

2. 先来看一个简单的泛型例子

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:14
 */

public class Pair<T> {
    T one;
    T two;

    public Pair(T one, T two) {
        this.one = one;
        this.two = two;
    }

    public T getOne() {
        return one;
    }

    public T getTwo() {
        return two;
    }
}

观察和普通类的区别:

  1. 类名后面多了一个<T>
  2. one 和 two的类型都是T

3. 那么T是什么呢?

T 表示类型参数

泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入。

现在我们知道了,泛型把类型作为了参数来使用。

4. 如何使用泛型,并将类型作为参数传入呢?

如下代码:我们分别new了三Pair对象,其中传入了不同类型的类型参数(Integer、Character、String)。

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:22
 */

public class Test {
    public static void main(String[] args) {

        Pair<Integer> pairInteger =  new Pair<Integer>(1,2);
        int one1 = pairInteger.getOne();
        int two1 = pairInteger.getTwo();
        System.out.println("one:"+one1+",two:"+two1);

        Pair<Character> pairCharacter  =  new Pair<Character>('一','二');
        char one2 = pairCharacter.getOne();
        char two2 = pairCharacter.getTwo();
        System.out.println("one:"+one2+",two:"+two2);

        Pair<String> pairString  =  new Pair<String>("I","II");
        String one3 = pairString.getOne();
        String two3 = pairString.getTwo();
        System.out.println("one:"+one3+",two:"+two3);
    }
}

结果如下:

one:1,two:2
one:一,two:二
one:I,two:II

当然我们不仅可以传入一个类型参数,也可以传入多个类型参数。多个类型参数之间用 逗号“,”隔开。如下面的例子:

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:37
 */

public class PairTwo <U,V> {
    U one;
    V two;

    public PairTwo(U one, V two) {
        this.one = one;
        this.two = two;
    }

    public U getOne() {
        return one;
    }

    public V getTwo() {
        return two;
    }
}

可以这么使用:

PairTwo<String,Integer> pairTwo = new PairTwo<>("牛牛",20);

注意:自 Java 7 开始,支持省略后面的类型参数,让书写更简单些。

5. 泛型的基本原理

泛型类型参数到底是什么?为什么一定要定义类型参数呢?定义普通类,直接使用Object也是可以呀。如之前的Pair类我们可以写成:

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:44
 */

public class PairObject {
    Object one;
    Object two;

    public PairObject(Object one, Object two) {
        this.one = one;
        this.two = two;
    }

    public Object getOne() {
        return one;
    }

    public Object getTwo() {
        return two;
    }
}

然后这样使用,也是同样的效果:

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:46
 */

public class TestPairObject {
    public static void main(String[] args) {
        PairObject pairObject1 = new PairObject(1,2);
        int one1 =(int)pairObject1.getOne();
        int two1 =(int)pairObject1.getTwo();
        System.out.println("one:"+one1+",two:"+two1);

        PairObject pairObject2 = new PairObject("yi","er");
        String one2 =(String)pairObject2.getOne();
        String two2 =(String)pairObject2.getTwo();
        System.out.println("one:"+one2+",two:"+two2);
    }
}

输出结果:

one:1,two:2
one:yi,two:er

我们可以看到,确确实实我们的使用Object + 强制类型转换也实现了相同的结果。事实上,我们Java泛型的内部原理就是这样的。

我们使用JAD工具来反编译我们的Pair.class 与 Test.class得到的结果如下:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:   Pair.java

package genericity.demo;
public class Pair
{
    public Pair(Object obj, Object obj1)
    {
        one = obj;
        two = obj1;
    }
    public Object getOne()
    {
        return one;
    }
    public Object getTwo()
    {
        return two;
    }
    Object one;
    Object two;
}

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:   Test.java

package genericity.demo;
import java.io.PrintStream;
// Referenced classes of package genericity.demo:
//            Pair

public class Test
{
    public Test()
    {
    }
    public static void main(String args[])
    {
        Pair pair = new Pair(Integer.valueOf(1), Integer.valueOf(2));
        int i = ((Integer)pair.getOne()).intValue();
        int j = ((Integer)pair.getTwo()).intValue();
        System.out.println((new StringBuilder()).append("one:").append(i).append(",two:").append(j).toString());
        Pair pair1 = new Pair(Character.valueOf('\u4E00'), Character.valueOf('\u4E8C'));
        char c = ((Character)pair1.getOne()).charValue();
        char c1 = ((Character)pair1.getTwo()).charValue();
        System.out.println((new StringBuilder()).append("one:").append(c).append(",two:").append(c1).toString());
        Pair pair2 = new Pair("I", "II");
        String s = (String)pair2.getOne();
        String s1 = (String)pair2.getTwo();
        System.out.println((new StringBuilder()).append("one:").append(s).append(",two:").append(s1).toString());
    }
}

通过以上的分析:

我们可以得知,Java的编译器将java源文件编译成字节码.class文件,虚拟机加载并运行。对于泛型类,java编译器会将其转换为普通的非泛型代码。将类型T擦除,然后替换为Object,插入必要的强制类型转换。Java虚拟机实际执行的时候,并不知道泛型这回事,只知道普通的类及代码。

那么为什么泛型要这样设计呢?

因为泛型是 Java 5 以后才支持的,这么设计是为了兼容性,而不得已的一个选择。

6. 使用泛型的好处

  • 代码复用:我们一套代码可以支持不同的类性。
  • 降低了耦合性:代码逻辑和数据类型之间分离,实现了解耦。
  • 更好的可读性:我们在使用集合的时候,定义了一个list 如List<String>,一看便知道这个一个存放String类型的list。
  • 更高的安全性:语言和程序设计的一个重要目标就是将bug消灭在摇篮里,能在写的时候消灭,就不要留在运行的时候。如我们定义一个List<String>这样的一个list。当我们往list里面放其他非String类型的数据时,我们的IDE(如Eclipse)就会报错提示。就算没有IDE。编译时,Java编译器也会提示,这称之为类型安全。这样就为程序设置了一道安全防护。同样的,使用泛型还可以省去使用普通对象时繁琐的强制类型转换。相反,使用普通对象,编译时并不会提示。假如传入的参数类型和最后强制类型转换的类型不一致。运行时就会出现ClassCastException,使用泛型则不会。

原文地址:https://www.cnblogs.com/nm666/p/11324345.html

时间: 2024-10-12 00:09:47

什么是泛型?泛型的基本原理与使用优势。的相关文章

.NET泛型01,为什么需要泛型,泛型基本语法

.NET泛型或许是借鉴于C++泛型模版,借助它可以实现对类型的抽象化.泛型处理,实现了类型和方法之间的解耦.一个最经典的运用是在三层架构中,针对不同的领域模型,在基接口.基类中实现针对各个领域模型的泛型处理. 本篇主要包括:■ 为什么需要泛型    ※ 不用泛型    ※ 使用泛型    ※ 泛型的运行时本质■ 泛型语法■ 典型的泛型类 为什么需要泛型 不用泛型 来看一个比较类型的方法. public class Calculator { public static bool AreEqual(

Java 泛型 泛型数组

直接创建泛型数组不能通过编译,而转型对象数组通过编译但是不能在JVM运行 public class ArrayOfGeneric{ static Generic<Integer>[] gia; @SupperssWarnings("unchecked") public static void main(String[] args){ gia = (Generic<Integer>[])new Generic[100]; // 通过类型转换匿名对象 //! gia

JAVA-初步认识-常用对象API(集合框架-泛型-泛型限定-上限的体现)

一. 现在要说应用,查看一下API中的注释, 这里有一个方法addAll里面有extedns E,我们看一下这个方法什么时候用.下面这三个集合,有存储person,student,还有worker的. 再接下来演示一下这个高级应用,如果我们要去想定义一下集合的话,(描述一下集合) 这个类里面加入的元素不确定的情况下,写的是个E,明确什么类型,add方法中添加的也是什么类型.一次添加一个元素比较慢,addAll方法可以添加一堆元素, 一般情况下,就是这么写.(addAll又将mycollectio

Java泛型/泛型限定

一.泛型概述: 1.来源:1.5jdk出现的新特性:用于解决安全问题,是一个安全机制: //下面代码,编译不报错,运行报错,加上泛型给与集合类型限定: 2.好处:减少运行时的问题,在编译时体现:避免强制转换的麻烦: 3.关键字:<数据类型> public class Test { public static void main(String[] args) { ArrayList arrayList = new ArrayList(); arrayList.add("1")

Java自学-泛型 泛型转型

Java 中的子类泛型转型成父类泛型 步骤 1 : 对象转型 根据面向对象学习的知识,子类转父类 是一定可以成功的 package generic; import charactor.ADHero; import charactor.Hero; public class TestGeneric { public static void main(String[] args) { Hero h = new Hero(); ADHero ad = new ADHero(); //子类转父类 h =

C#知识 泛型

C#知识 泛型 泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能.泛型将类型参数的概念引入 .NET Framework,类型参数使得设计如下类和方法成为可能:这些类和方法将一个或多个类型的指定推迟到客户端代码声明并实例化该类或方法的时候.例如,通过使用泛型类型参数 T,您可以编写其他客户端代码能够使用的单个类,而不致引入运行时强制转换或装箱操作的成本或风险,避免进行强制类型转换的需求提高类型安全性. 泛型概述及优点 泛型类和泛型方法同时具备可重用性.类型安全和效率高等

Java泛型学习笔记 - (一)泛型的介绍

一.什么是泛型:泛型的作用是用来规定一个类, 接口或方法所能接受的数据的类型. 就像在声明方法时指定参数一样, 我们在声明一个类, 接口或方法时, 也可以指定其"类型参数", 也就是泛型. 不同的是, 声明方法时我们给其参数指定一个值, 而给其泛型指定一个数据类型.二.基本使用方式: 上面的概念啰嗦了许多, 其实我自己写的都累. 最简单有效的学习方法就是用一用嘛: 1 List<String> list = new ArrayList<String>(); 这就

泛型--面向对象7

课程概要 认识泛型 构造方法中使用泛型 设置多个泛型 通配符 泛型接口 泛型方法 泛型数组 1.泛型是在JDK1.5增加的功能 泛型(Generic) 2.泛型可以解决数据类型的安全性问题(类型不一致例如定义private Object float px=(Float)p.getX()), 它的原理是在类声明的时候通过标识符 表示类中的某个属性的类型和某个方法的返回值及参数类型. 3.格式: 访问权限 class类名称<泛型,泛型> {属性 方法} 4.对象的创建 类名称<具体类型>

Java 容器 &amp; 泛型:六、容器讲到为什么要使用泛型

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket ArrayList是集合类中无处不在的,泛型也是,泛型对集合类尤其有用.但是为啥要使用泛型?理解好了这个问题可以帮助理解相关的更多知识点.下面泥瓦匠以最简单的例子来验证这个问题. 一.泛型 泛型的目的是为了可以让更多不同类型的对象重用.没错,这样理解就太low.真正目的是为了在编译时找到bug,而不是在运行时.(编译时,指的是源代码翻译成机器识别的代码的时候.运行时,是指代码在机器中运行的时候.)泛