【java基础】跟着 Oracle java doc 学习 <1> 泛型

前端之家收集整理的这篇文章主要介绍了【java基础】跟着 Oracle java doc 学习 <1> 泛型前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1. 泛型 (Generics)介绍 详情参看 Oracle 官方文档

泛型 使更多的 bug 在 编译时期就可以被发现 , 为代码增加稳定性 。

泛型带来的好处 :

★ 在编译时期 强类型检查

★ 转换的减少

★ 使程序实现了 泛型算法 ,可以自定义,更容易使用,类型安全的,并且更容易读


1.1. 泛型 类型 (Gerneric Types)

泛型类型 是 一个通过类型参数化的 泛型 类或者接口 。

泛型 类 的定义形式如下 : 类型参数 被 <> 包围 ,紧跟在 类名的后面 ,声明了形参(type parameters 也被成为类型变量 type variables)为 T1,T2...Tn,类型变量可以是指定的任意非原始类型 ;同样适用于接口

  1. class name<T1,T2,...,Tn> { /* ... */ }


非泛型和 泛型 栗子 :

非泛型 Box

  1. /**
  2. * Created by xlch on 2016/7/21.
  3. */
  4. public class Box {
  5. private Object object;
  6.  
  7. public Object getObject() {
  8. return object;
  9. }
  10.  
  11. public void setObject(Object object) {
  12. this.object = object;
  13. }
  14.  
  15. @Test
  16. public void test(){
  17. Box Box = new Box();
  18. Box.setObject(2); //可以传任何类型的值
  19. Box.setObject("333");
  20. }
  21. }


泛型类 Box

  1. /**
  2. * Created by xlch on 2016/7/21.
  3. */
  4. public class Box<T> { //泛型类型声明
  5.  
  6. // T 代表 类型参数
  7. private T t;
  8.  
  9. public void set(T t){
  10. this.t = t;
  11. }
  12.  
  13. public T get(){
  14. return t;
  15. }
  16.  
  17. @Test
  18. public void test(){
  19. Box<Integer> Box = new Box<>(); // JAVA SE 7 的钻石语法:可以省略后面 <> 中的类型参数
  20. Box.set(222); // 只能传递 Integer 类型的值,否则编译期就会报错
  21. }
  22. }


按照惯例 , 类型参数 的命名规则是 : 一个大写字母 ,最常用的 类型参数名称如下 :

★ E - Element

★ K - Key

★ T - Type

★ N - Number

★ V -Value

★ S ,U,V - 2nd,3trd,4th


调用和实例化泛型类型

调用泛型类型时 ,必须执行泛型类型调用 ,即使用实际的类或者接口代替 类型参数(如 T) :参数化类型(parameterized type)

  1. Box<Integer> integerBox; //和普通的变量声明一样,声明了一个持有 "Integer"引用的 integerBox 变量
实例化泛型类 :
  1. Box<Integer> integerBox = new Box<Integer>();
  2. Box<Integer> integerBox = new Box<>(); //java SE 7 的 钻石语法 (Diamond)



·type Parameter 和 Type Argument 术语 :这两个术语的意思是不相同的 。 提供 type arguments 的目的是为了创建参数化的类型。因此,Foo<T> 的 T 属于 type parameter,Foo<String> 的 String 属于 type argument .有点形参实参的感觉。


可以定义单个类型参数 ,也可以定义多个类型参数 (如JAVA SE 中集合的定义)


1.2. 原始类型 (Raw Type) :在调用或者实例化泛型类型时没有类型参数 的类或者接口

栗子 : 应该避免使用 原始类型


  1. /**
  2. * Created by xlch on 2016/7/21.
  3. */
  4. public class Foo<T> {
  5.  
  6. public void set(T t){
  7. System.out.println(t);
  8. }
  9.  
  10. @Test
  11. public void test(){
  12. Foo<Integer> foo = new Foo<>();
  13. foo.set(4);
  14.  
  15. Foo foo1 = new Foo(); // 这个 Foo 便是 Foo<Integer> 的 原始类型 ,raw type
  16. foo1.set("333"); // ok
  17. foo1.set(33); // ok
  18. Foo foo2 = foo; // 会有警告
  19. foo2.set("22"); // Unchecked call set...的警告
  20. Foo<Integer> foo3 = foo1; //会有警告
  21. }
  22. }

1.3 . 泛型方法 (Generic Method)

利用类型参数 定义的方法 ,类型参数只能该方法可以使用 ,静态和非静态 泛型以及 泛型构造函数都是可以的 。

语法要求:

★ 括号内使用类型参数

方法返回类型之前使用类型参数 ,而对于静态的泛型方法 , 类型参数必须出现在方法的返回类型之前 ,static 关键字之后 ;此处的 类型参数可以使用类型的限制 ,extends or super

栗子 :

泛型类 :

  1. /**
  2. * Created by xlch on 2016/7/21.
  3. */
  4. public class Pair<K,V> {
  5. private K k; //类型参数 type parameter
  6. private V v; //类型参数
  7.  
  8. public Pair(K k,V v) {
  9. this.k = k;
  10. this.v = v;
  11. }
  12.  
  13. public K getK() {
  14. return k;
  15. }
  16.  
  17. public void setK(K k) {
  18. this.k = k;
  19. }
  20.  
  21. public V getV() {
  22. return v;
  23. }
  24.  
  25. public void setV(V v) {
  26. this.v = v;
  27. }
  28. }

静态方法 util 类
  1. /**
  2. * Created by xlch on 2016/7/21.
  3. */
  4. public class Util {
  5.  
  6. // 静态泛型方法
  7. public static <K,V> boolean compare(Pair<K,V> p1,Pair <K,V> p2){
  8. return p1.getK().equals(p2.getK()) && p1.getV().equals(p2.getV());
  9. }
  10.  
  11.  
  12. @Test
  13. public void test(){
  14. Pair<Integer,String> p1 = new Pair<>(1,"apple");
  15. Pair<Integer,String> p2 = new Pair<>(1,"apple");
  16. boolean same = Util.<Integer,String>compare(p1,p2);
  17. boolean equal = Util.compare(p1,p2); //可省略 ,类型推断 type inference
  18. System.out.println(same);
  19. System.out.println(equal);
  20. }
  21. }


1.4. 受限的类型参数 :即限制类型参数


即限制 方法参数的传入类型 ,这里使用 extends 关键字,但是这里的 extends 和 类或者 接口的继承是不同的 。

  1. /**
  2. * Created by xlch on 2016/7/22.
  3. */
  4. public class Box<T> {
  5.  
  6. private T t;
  7.  
  8. public T getT() {
  9. return t;
  10. }
  11.  
  12. public void setT(T t) {
  13. this.t = t;
  14. }
  15.  
  16. public <U extends Number>void inspect(U u){ // extends 类型参数限制 ,只能传入 Number的实例 或者 Number 的子类实例
  17. System.out.println(u);
  18. }
  19.  
  20. @Test
  21. public void test(){
  22. Box<Integer> Box = new Box<>();
  23. Box.inspect("hi");//这时候会报错,String 类型不能作为传入的参数类型
  24. }
  25. }

多个限制的使用语法 :

使用 & 连接 ,但是如果限制中有一个是类,则必须第一个被指定,其他接口跟在后面,否则报错 。 形如 :

  1. <T extends B1 & B2 & B3>


栗子 :

  1. Class A { /* ... */ }
  2. interface B { /* ... */ }
  3. interface C { /* ... */ }
  4.  
  5. class D <T extends A & B & C> { /* ... */ } // 类必须放在第一个 ,后面放接口,否则报错
  6.  
  7. class D <T extends B & A & C> { /* ... */ } // compile-time error



1.5 泛型 , 继承 ,子类

  1. Object someObject = new Object();
  2. Integer someInteger = new Integer(10);
  3. someObject = someInteger; // OK
  4.  
  5.  
  6. public void someMethod(Number n) { /* ... */ }
  7.  
  8. someMethod(new Integer(10)); // OK
  9. someMethod(new Double(10.1)); // OK
  10.  
  11.  
  12. Box<Number> Box = new Box<Number>();
  13. Box.add(new Integer(10)); // OK
  14. Box.add(new Double(10.1)); // OK
  15.  
  16.  
  17. public void BoxTest(Box<Number> n) { /* ... */ }
  18. BoxTest(Box<Number> nBox); //ok
  19. BoxTest(Box<Integer> iBox); // wrong

关系图如图 :

泛型类和子类 :



1.6 类型推断 (Type inference)


类型推断和 泛型方法 : 例如静态泛型方法调用

  1. BoxDemo.<Integer>addBox(Integer.valueOf(10),listOfIntegerBoxes); //完全写法
  2.  
  3. BoxDemo.addBox(Integer.valueOf(20),listOfIntegerBoxes); //可省略 ,java 编译时期自动类型推断 类型为 Integer

类型推断和 实例化泛型类 : java SE 7 推出的钻石语法
  1. Map<String,List<String>> myMap = new HashMap<String,List<String>>();
  2. Map<String,List<String>> myMap = new HashMap<>(); //钻石语法
  3. Map<String,List<String>> myMap = new HashMap(); // raw types ,会有警告,不建议这样使用

类型推断 和 泛型/非泛型的泛型构造函数
  1. class MyClass<X> {
  2. <T> MyClass(T t) {
  3. // ...
  4. }
  5. }

  1. new MyClass<Integer>("")

目标类型

如 Collections 有以下 静态泛型方法

  1. static <T> List<T> emptyList();

调用
  1. List<String> listOne = Collections.emptyList(); // 则实例 List<String> 为 target type
  2. List<String> listOne = Collections.<String>emptyList(); // 完全写法
但是 ,如果 有这样的方法
  1. void processStringList(List<String> stringList) {
  2. // process stringList
  3. }
  4. processStringList(Collections.emptyList()); //List<Object> cannot be converted to List<String> java SE8 可以正常编译
  5. processStringList(Collections.<String>emptyList()); //java SE 7及 之前的 JDK 版本 必须指定类型参数的值




2. 通配符 (Wildcards )

? 被称为通配符 ,代表一种未知的类型(Type),可以被用在各种情形 : 参数 、字段 、本地变量 ,有时候也会作为类型返回 。但是 ,通配符不会使用在下列的情况 : 泛型方法的类型参数 、泛型类实例的创建、或者父类型 。


2.1 上限通配

形如 <? extends A> A 类型 或者 A 类型的子类型

栗子如下 :

  1. public static double sumOfList(List<? extends Number> list) { // 只能Number 类型的实例或者 Number 的子类的实例
  2. //...

2.2 没有受限的通配符 :形如 : List<?>

如下两个场景可能会使用 :

★ 如果写 一个方法 ,该方法可以使用提供的 Object 类型

★ 泛型 类中 使用的代码不依赖于 类型参数 ,如 list.size(),即 Class<T> 不依赖于 T

栗子 :

  1. /**
  2. * Created by xlch on 2016/7/22.
  3. */
  4. public class Upper {
  5.  
  6. public static void out(List<Object> list){
  7. for (Object object:list){
  8. System.out.println(object);
  9. }
  10. }
  11.  
  12. public static void print(List<?> list){
  13. for (Object object:list){
  14. System.out.println(object);
  15. }
  16. }
  17.  
  18. @Test
  19. public void test(){
  20. List<Integer> list = Arrays.asList(1,2,3);
  21. out(list); //编译期会报错, 必须是List<Object>
  22. print(list); //编译期不会报错
  23. }
  24. }

2.3 下限的通配

形如 <? super A> : A 类型 或者 A 类型 的父类

<? extends A> : A 类型 或者 A 类型 的子类


2.4 通配符和子类的关系 (具有协变性)


如下图 : 虽然 Integer 是 Number 的子类 ,但是 List<Integer> 和 List<Number> 是不相关的,即泛型 他们公共父类 是 List<?>

  1. List<String> strings = new ArrayList();
  2. List<?> wildCards = new ArrayList<>();
  3. List<Object> objects = new ArrayList<>();
  4. wildCards = strings; // 通配符 ok
  5. objects = strings; // 泛型 报错 Incompatible types

2.5 通配符匹配 和 帮助方法

小栗子 :

  1. void foo(List<?> i) {
  2. // i.set(0,i.get(0)); // 报错
  3. helper(i);
  4. }
  5.  
  6. private <T> void helper(List<T> t){
  7. t.set(0,t.get(0));
  8. }



3 类型 擦除 (Type)

为了实现 泛型 ,Java 编译器 运用了 类型擦除 :

★ 使用 界限类或者 Object(如果 类型参数没有界限) 替换 泛型类中所有的 类型参数 。 因此可以看作是普通的 类 / 接口 / 方法

★ 必要时 插入类型的强制转换 以保证类型的安全

★ 在扩展的泛型 类中 生成桥接方法以保存多态性

类型擦除 保证 为了 参数化 类型没有 新的类创建 ,因此泛型不会产生运行时开销 。


3.1 泛型类的擦除

栗子 :

泛型类

  1. public class Node<T> {
  2.  
  3. private T data;
  4. private Node<T> next;
  5.  
  6. public Node(T data,Node<T> next) }
  7. this.data = data;
  8. this.next = next;
  9. }
  10.  
  11. public T getData() { return data; }
  12. // ...
  13. }
擦除后的类 (没有界限,所以用 Object 替换类型参数):
@H_522_301@public class Node { private Object data; private Node next; public Node(Object data,Node next) { this.data = data; this.next = next; } public Object getData() { return data; } // ... }

3.2 泛型方法的擦除

栗子:

  1. public static <T> int count(T[] anArray,T elem) {
  2. int cnt = 0;
  3. for (T e : anArray)
  4. if (e.equals(elem))
  5. ++cnt;
  6. return cnt;
  7. }

擦除后:
  1. public static int count(Object[] anArray,Object elem) {
  2. int cnt = 0;
  3. for (Object e : anArray)
  4. if (e.equals(elem))
  5. ++cnt;
  6. return cnt;
  7. }

猜你在找的Oracle相关文章