Java基础知识之泛型全接触

当我们在定义类,接口和方法时,可以接收一个类型作为参数,这就叫做泛型

函数可以传入普通的参数,也可以传入一个类型参数。不同之处是普通的参数就是值而已,但是类型参数却是个类型。

使用泛型的好处:

  • 强类型检查。在编译时就可以得到类型错误信息。
  • 避免显式强制转换。
  • 方便实现通用算法。

对类使用泛型

我们可以创建一个简单的Class Box。它提供存取一个类型为Object的对象。

1
2
3
4
5
6
7
8
9
10
public class Box {
    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    private Object object;

你可以传给它任何你想要的对象,比如对象String,Integer等,也可以传入自定义的一些对象。但是调用getObject方法返回的对象需要显式的强转为传入的类型,才能使用原来类型的一些方法。

我们可以使用泛型来构造这个对象。

1
2
3
4
5
6
7
8
9
10
11
public class Box<T> {
    public T getObject() {
        return object;
    }

    private T object;

    public void setObject(T object) {
        this.object = object;
    }
}

我们可以看到,所有的Object被替换成了T。T代表了某种类型,你在实例化Box对象时,必须要给其指定一种类型,String,Integer或者自定义的类,并且调用getObject方法并不需要进行强转就可以使用该类型的方法。

一般来说,类型参数名称越简单越好,并且需要是大写的。为了方便,我们约定了一些命名使用。

  • E Element
  • K key
  • N Number
  • T type
  • V value
  • S,U,V 第2,3,4个类型

我们可以这样实例化一个Box类。

1
Box<Integer> integerBox = new Box<Integer>();

同样,我们也支持在一个类中传入多个类型参数。例如下面的Pair对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Pair<T, V> {
    private T key;
    private V value;

    public Pair(T key, V value) {
        this.key = key;
        this.value = value;
    }

    public T getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

使用方法如下。

1
2
3
Pair<Integer, String> one = new Pair<Integer, String>(1, "one");

Pair<String, String> hello = new Pair<String, String>("hello", "world");

对方法使用泛型

泛型可以作用与方法上,此时泛型参数只能在方法体中使用。而泛型作用于类时,则在整个类中可以使用。

在静态方法、非静态方法及构造函数都可以使用泛型。

1
2
3
4
5
6
7
public class Util {

    public static <T, U> boolean compare(Pair<T,U> pair1, Pair<T,U> pair2)
    {
        return pair1.getKey().equals(pair2.getKey()) && pair1.getValue().equals(pair2.getValue());
    }
}

下面是对该静态方法的使用。

1
2
3
4
5
       Pair one = new Pair("one", 1);
        Pair two = new Pair("two", 2);

        assertThat(Util.compare(one, two), is(false));
// pass

对泛型进行限定

默认情况下如果直接使用的话,我们可以给其传任何值。有时候我们想值允许传入某个类及它的子类。这时候在声明泛型时可以使用extends关键字。

1
2
3
4
5
6
7
8
9
10
11
public class Box<T extends Number> {
    public T getObject() {
        return object;
    }

    private T object;

    public void setObject(T object) {
        this.object = object;
    }
}
1
2
3
      Box box = new Box();
        box.setObject(10);    //ok
        box.setObject("hello");  //compile-time error

我们也可以给类型参数加多个限定。

1
<T extends B1 & B2 & B3>

加上限定类或接口以后,我们可以使用泛型参数变量调用该类或接口的方法。

通配符的使用

Java中的List就是一个实现了泛型的类,假如我们写了一个方法,获取List中元素的个数。只不过这个方法限定T类型为Number。

1
2
3
4
5
6
7
8
9
  public static int getCount(List<Number> list)
  {
      int i = 0;
      for(Number n : list)
      {
          i++;
      }
      return i;
  }

然后我们这样试图调用它。

1
2
3
4
5
6
7
8
9
    List<Integer> list = new ArrayList<Integer>(){
        {
            add(1);
            add(2);
            add(3);
        }
    };

    Util.getCount(list); //compile-time error

为什么会产生错误那?因为我们要求方法的参数是List,而我们实际传入的是List。虽然Integer是Number的子类,但是List却不是List的子类,他们其实是平等的关系。这点一定要注意。我们在方法定义时已经明确表示T的类型是Number了,所以只能接收List,而不能接收其它类型的参数。
这时候?通配符就起作用了。我们可以使用通配符重新定义这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Util {

  public static int getCount(List<? extends Number> list)
  {
      int i = 0;
      for(Number n : list)
      {
          i++;
      }
      return i;

  }
}
1
2
3
4
5
6
7
8
9
    List<Integer> list = new ArrayList<Integer>(){
        {
            add(1);
            add(2);
            add(3);
        }
    };

    assertThat(Util.getCount(list), is(3));  // pass

既然能限定到一个类及其子类上,当然也能限定到一个类及其父类上。语法如下:

1
<? supper A>

对泛型使用的总结

  • 类型参数不能是原始类型(int, char,double),只能传入这些类型的封转类(Integer,Char,Double)。
  • 不能直接创建类型参数的实例。

1
2
public static <E> void append(List<E> list) {    E elem = new E();  // compile-time error    list.add(elem);
}

但有通过反射可以实现。

1
public static <E> void append(List<E> list, Class<E> cls) throws Exception {    E elem = cls.newInstance();   // OK    list.add(elem);}

你可以这样调用它:

1
2
List<String> ls = new ArrayList<>();
append(ls, String.class);
  • 静态字段的类型不能为类型参数。

1
2
3
public class Box<T> {    private static T object; // compile-time error

}
  • 不能创建类型参数变量的数组。

1
List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error
  • 不能重载一个方法,该方法的形参都来自于同一个类型参数对象。

1
2
3
4
5
6
public class Example {

  public void print(List<Integer> integers) {}

    public void print(List<Double> doubles) {}
}

参考文档:http://docs.oracle.com/javase/tutorial/java/generics/index.html

时间: 2024-10-22 09:07:54

Java基础知识之泛型全接触的相关文章

Java基础知识之Enum全接触

Enum是在Java中用来定义枚举类型的关键字.Enum一般用来表示一组相同类型的常量,如性别.日期 .颜色等. 下面是一个最简单的枚举. 1 2 3 4 5 6 7 8 9 10 public enum Color { RED, GREEN, BLUE } 实际上在Java中枚举类型本质上就是一个类,其继承自java.lang.Enum类. 通过默认提供的GetValues()方法可以获取所有枚举对象,其以一个数组的形式返回. 1 2 3 4 5 6 for(Color color : Col

《Java和Android开发实战详解》——1.2节Java基础知识

1.2 Java基础知识 Java和Android开发实战详解 Java语言类似于C++是一种编译型语言,不过两者并不完全相同,严格说来,Java是结合编译和解释优点的一种编程语言. 1.2.1 Java平台 "平台"(Platform)是一种结合硬件和软件的执行环境.Java既是一种高级的面向对象的编程语言,也是一个平台.Java平台是一种纯软件平台,它可以在各种基于硬件的平台上运行,与硬件无关,主要是由JVM和Java API两个部分组成. 1.JVM虚拟机 JVM(Java Vi

J2ME中需要的Java基础知识

现在有大部分人,都是从零开始学J2ME的,学习J2ME的时候,总是从Java基础开始学习,而且现在讲Java基础的书籍中都是以J2SE来讲基础,这就给学习造成了一些不必要的麻烦,下面将J2ME中用到的和不需要的Java基础知识做一个简单的说明:        J2ME中使用到的Java基础知识: 1.  Java语法基础:包括基本数据类型.关键字.运算符等等 2.  面向对象的思想:类和对象的概念,继承和多态等等. 3.  异常处理 4.  多线程 J2ME中没有用到的Java基础知识: 1. 

javaeye基础-java基础知识问题求助

问题描述 java基础知识问题求助 问 形如 X(父类) y=new Y(子类): 这样new出来的对象y是子类对象还是父类对象? 如果子类有重载的构造函数,那么在new的时候父类的默认构造函数还会被子类的构造函数调用么? 解决方案 你可以去了解下向上转型和向下转型的区别! 解决方案二: java的一些基础知识 .Java基础知识一Java_计算机基础知识 解决方案三: new 出来的当然是子类对象,但是是父类引用,即父类引用指向之类对象. 子类有重载的构造函数?构造函数是不能重载的 解决方案四

[C++ 面试基础知识总结] 泛型算法

[C++ 面试基础知识总结] 泛型算法 参考书籍:<C++ Primer> 目录 C 面试基础知识总结 泛型算法 目录 基础泛型算法 只读算法 写容器算法 重排容器元素算法 定制操作 向算法传递函数 lambda表达式 参数绑定 特殊迭代器 插入迭代器 iostream迭代器 反向迭代器 5类迭代器 链表的特定容器算法 基础泛型算法 泛型算法本身运行于迭代器之上,不会执行容器的操作,可以改变容器中保存元素的值,也可以在容器内移动元素,但永远不会直接添加或删除元素.插入迭代器是一种特殊的迭代器,

《Android游戏开发详解》一第2章 Java基础知识

第2章 Java基础知识 Android游戏开发详解第1章内容完全是成为Java程序员的准备工作.在本章中,你将编写自己的第一个Java程序(包括一款简单的游戏),并学习如何把游戏的角色.加血(power-up)以及其他实体表示为Java对象.

《Android游戏开发详解》一第1部分 Java基础知识

第1部分 Java基础知识 Android游戏开发详解本文仅用于学习和交流目的,不代表异步社区观点.非商业转载请注明作译者.出处,并保留本文的原始链接.

Java基础知识二十九

Java 基础语法 一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作.下面简要介绍下类.对象.方法和实例变量的概念. 对象:对象是类的一个实例,有状态和行为.例如,一条狗是一个对象,它的状态有:颜色.名字.品种:行为有:摇尾巴.叫.吃等. 类:类是一个模板,它描述一类对象的行为和状态. 方法:方法就是行为,一个类可以有很多方法.逻辑运算.数据修改以及所有动作都是在方法中完成的. 实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定. 如果你想学

java基础知识

java语言基础 (一些大家需要了解的知识,或者说学java的需要知道的知识) java需要的api大家可以看下我的关于java API 的文章里面有最新的javaapi(是1.8 的中文版的) 1.java历史 Java语言发展史     詹姆斯·高斯林(James Gosling)1977年获得了加拿大卡尔加里大学计算机科学学士学位,1983年获得了美国卡内基梅隆大学计算机科学博士学位,毕业后到IBM工作,设计IBM第一代工作站NeWS系统,但不受重视.后来转至Sun公司,1990年,与Pa