Java - 泛型的理解2

http://www.lxweimin.com/p/7e3e2b898143
這是上次寫的泛型,當時其實還是一知半解。
今天再做個小總結,但也還是一知半解吧。
今天老師上課,講了很多泛型的東西。
generics

第一個東西,叫做 type parameter
其中有部分,我到現在還是不懂。

<T>void print(Collection<T> c) {
  for (T x : c)
      print("elem " + x);

}

至于這個,void 左邊的 <T>到底是干什么的,老師給我解釋了兩遍還是不能很理解。他的意思就是,這是一種規矩。

public class Demo<T>{
     public T a;
     public Demo(T k) {
            a = k;
      }

      <T>void change(T k) {
                a = k;
       }
}

這樣是有編譯錯誤的。
如果把void 左邊<T>去掉就對了。
所以我當時做的結果是,如果這個函數是要修改自己的元素,那么不能加<T>,如果是要修改外面傳進來的元素,那么就加<T>,不會報錯。
比如,

<T> void change(T k) {
     k = null;
}

但是老師說不是。他說我第一個為什么會錯呢?只要把<T>換個名字不和T重復就行了。我換了,果然就對了。為什么呢?下次office hour得去問下。

下面進入一個正題。

ArrayList<Object> b = new ArrayList<Object>();
b.add(5);
b.add("abc");

char a = (char) b.get(0);
編譯時不報錯,運行時報錯。
generics是編譯時可見,運行時擦除的。
所以編譯的時候,編譯器知道,b.get(0)返回的是一個Object類型,語法上可以被強制轉換成其他類型。于是就通過了。運行時,擦除了<T>的信息。于是,系統嘗試著將Object類型強制轉換成char。但是我們都知道,這個object對象的內存塊,本質是Integer。
所以,只能被強制轉換成Integer,不能被強制轉換成char。
于是報錯了。

char a = b.get(0);
編譯時報錯。
因為編譯時是知道generics的<T>的。所以一匹配,一邊是char,一邊是object,不匹配,直接編譯錯誤,static error

Integer a = b.get(0);
編譯錯誤,不能自動cast。這是超類轉子類,必須cast

Integer a = (Integer) b.get(0);
編譯通過,運行通過。

改一下,
ArrayList<Integer> b = new ArrayList<Integer>();
b.add(5);
Object a = b.get(0);
Object c = (Object) b.get(0);
都是對的。
子類轉超類,不需要cast,會自己轉。

然后是上篇文章討論的問題。
LinkedList<ArrayList<Integer>>[] a = new LinkedList<ArrayList<Integer>>[100];
這個在編譯時是無法通過的。
因為數組是運行時才會開始著手考慮元素類型的問題。但是這個時候<T>已經被擦除了,返回的內存可能不安全。于是Java提前在編譯時就把這個錯誤找了出來,提前規避這個風險。
但其實,我測試過,
int[] a = new int[5];
a[0] = "abc";
a[100] = 5;
第一個是編譯錯誤,第二個是運行錯誤。
也就是說,在編譯時,數組是可以知道,他的元素的種類的。
我還過另外一個說法。這里為什么會編譯錯誤,因為,Java的作者當時忘記寫這塊了。。。于是乎。。。
不管怎么樣,記得不能這么用吧。具體改造方法那篇文章里有。
其中,強制轉換是這樣的,

LinkedList<ArrayList<Integer>>[] c = (LinkedList<ArrayList<Integer>>[]) new LinkedList[100];

或者,采用ArrayList來做。

List<Object> a = new ArrayList<String>();

為什么是不對的?

List<String> stringList = new ArrayList<String>();
List<Object> objectList = stringList ;
objectList .add(new Integer(5));
String temp = stringList.get(0);

按道理,stringList.get(0); 應該返回一個String
但是這里返回的是一個Integer
為了增加安全性,Java同樣決定在編譯時,就提前規避這種風險,不讓這種東西可以被寫出來,可以通過編譯,可以運行。
但是可以這么改。

List<Object> objectList = (List<Object>) (List<?>) stringList;

先轉換成,一種不知道什么類型的類型。在轉換成object。
但是這是有警告的,而且如果你之前不知道所存的元素本質是什么,這么用是很危險的。
所以,<>所存放的東西,他們必須,完全一致。

List<List<Integer>> = new ArrayList<ArrayList<Integer>>(); // ERROR!
List<List<Integer>> = new ArrayList<List<Integer>>(); // RIGHT!

然后老師提到了這么一個運算符 <?>
LinkedList<?> means LinkedList<T> for some T
但是,之后這個鏈表返回的元素,只能用Object來接收,因為他自己都不知道,他返回的是什么東西。
Object o = llist.first();
還可以這么用,
LinkedList<? extends Puzzle> means LinkedList<T> for some T extends <Puzzle>

使用泛型的限制。

不能使用primitive types as T
比如, Collection<int> 錯誤!!!

cannot create a T like:
new T();
or
T[] a = new T[n];
因為在運行時會把編譯時知道的T信息擦除掉,所以不知道T到底是什么,不知道怎么申請什么樣的內存。

cannot inspect T at run time
like
instance of T
instance of Collection<String>
因為編譯時,不知道<T> 是String

最后說下,Comparator
這塊只是一直沒怎么學習過。
Comparator是一個interface
里面有兩個方法,

public class A implements Comparator<T> {
        public int compare(T a, T b) {....}     
}

因為傳入的T不知道類型,這是復雜版本,不知道怎么處理。
如果傳入的類知道類型,復雜的情況,就是不能簡單地用java系統方法,compareTo,就得自己寫一個compareTo去判斷大小。
簡單地話,傳進來的類自己實現了Comparable 接口,于是直接調用compareTo() 方法就行了。
如:

public class A implements Comparator<Integer> {

public int compare(Integer a, Integer b) {
            if (a.compareTo(b) < 0)
                    return -1;
            else if (a.compareTo(b) > 0)
                    return 1;
            else
                    return 0;
    }   
}

然后在一個新類中,

public class B {

      public void sort(int[] a, Comparator<Integer> q) {
        for (int i = 1; i < a.length; i++) {
            for (int j = i; j >= 1; j--) {
                if (q.compare(a[j - 1], a[j]) > 0) {
                    int temp = a[j];
                    a[j] = a[j - 1];
                    a[j - 1] = temp;                }
            }
        }
    }

      public static void main(String[] args) {
            int[] array = {3, 4, 2, 6,19, 1};
            B test = new B(); 
            test.sort(array, new A());
      }

}

就可以實現排序了。
Comparator 比較的是兩個數的大小,他的角度是,客觀的看到一串數,是旁觀者。
public int compare(T a, T b) {....}
Comparable 比較的是自己和另外一個數的大小,他的角度是,當局者,他只看到另外一個數。
public int compareTo(T other) {....}
他們都是接口。主要就是本身的意義不同。

然后還有個問題。
Arrays.sort(array, new A());
是報錯的,那么Arrays.sort()這個方法該如何使用comparator呢?我得去問問老師。
就先寫到

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,769評論 0 33
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,141評論 0 62
  • 小編費力收集:給你想要的面試集合 1.C++或Java中的異常處理機制的簡單原理和應用。 當JAVA程序違反了JA...
    八爺君閱讀 4,656評論 1 114
  • 豆瓣博主 企業好好做相關的技術帖或者行業風險剖析和揭露 知乎大V 看知乎工具 微博 三大段子手 項目立案 預算和目...
    史成玉閱讀 220評論 0 0