国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

教妹學(xué) Java:晦澀難懂的泛型

Crazy_Coder / 3684人閱讀

摘要:首先,我們來(lái)按照泛型的標(biāo)準(zhǔn)重新設(shè)計(jì)一下類。注意參數(shù)為而不是泛型。利用形式的通配符,可以實(shí)現(xiàn)泛型的向上轉(zhuǎn)型,來(lái)看例子。需要注意的是,無(wú)法從這樣類型的中取出數(shù)據(jù)。

00、故事的起源

“二哥,要不我上大學(xué)的時(shí)候也學(xué)習(xí)編程吧?”有一天,三妹突發(fā)奇想地問(wèn)我。

“你確定要做一名程序媛嗎?”

“我覺(jué)得女生做程序員,有著天大的優(yōu)勢(shì),尤其是我這種長(zhǎng)相甜美的。”三妹開始認(rèn)真了起來(lái)。

“好像是啊,遇到女生提問(wèn),我好像一直蠻熱情的。”

“二哥,你不是愛(ài)好寫作嘛,還是一個(gè) Java 程序員,不妨寫個(gè)專欄,名字就叫《教妹學(xué) Java》。我高考完就開始跟著你學(xué)習(xí)編程,還能省下一筆培訓(xùn)費(fèi)。”三妹看起來(lái)已經(jīng)替我籌劃好了呀。

“真的很服氣你們零零后,蠻有想法的。剛好我最近在寫 Java 系列的專欄,不妨試一試!”

PS:親愛(ài)的讀者朋友們,我們今天就從晦澀難懂的“泛型”開始吧!(子標(biāo)題是三妹提出來(lái)的,內(nèi)容由二哥我來(lái)回答)

01、二哥,為什么要設(shè)計(jì)泛型啊?

三妹啊,聽哥慢慢給你講啊。

Java 在 5.0 時(shí)增加了泛型機(jī)制,據(jù)說(shuō)專家們?yōu)榇嘶ㄙM(fèi)了 5 年左右的時(shí)間(聽起來(lái)很不容易)。有了泛型之后,尤其是對(duì)集合類的使用,就變得更規(guī)范了。

看下面這段簡(jiǎn)單的代碼。

ArrayList list = new ArrayList();
list.add("沉默王二");
String str = list.get(0);

但在沒(méi)有泛型之前該怎么辦呢?

首先,我們需要使用 Object 數(shù)組來(lái)設(shè)計(jì) Arraylist 類。

class Arraylist {
    private Object[] objs;
    private int i = 0;
    public void add(Object obj) {
        objs[i++] = obj;
    }
    
    public Object get(int i) {
        return objs[i];
    }
}

然后,我們向 Arraylist 中存取數(shù)據(jù)。

Arraylist list = new Arraylist();
list.add("沉默王二");
list.add(new Date());
String str = (String)list.get(0);

你有沒(méi)有發(fā)現(xiàn)兩個(gè)問(wèn)題:

Arraylist 可以存放任何類型的數(shù)據(jù)(既可以存字符串,也可以混入日期),因?yàn)樗蓄惗祭^承自 Object 類。

從 Arraylist 取出數(shù)據(jù)的時(shí)候需要強(qiáng)制類型轉(zhuǎn)換,因?yàn)榫幾g器并不能確定你取的是字符串還是日期。

對(duì)比一下,你就能明顯地感受到泛型的優(yōu)秀之處:使用類型參數(shù)解決了元素的不確定性——參數(shù)類型為 String 的集合中是不允許存放其他類型元素的,取出數(shù)據(jù)的時(shí)候也不需要強(qiáng)制類型轉(zhuǎn)換了。

02、二哥,怎么設(shè)計(jì)泛型啊?

三妹啊,你一個(gè)小白只要會(huì)用泛型就行了,還想設(shè)計(jì)泛型啊?!不過(guò),既然你想了解,那么哥義不容辭。

首先,我們來(lái)按照泛型的標(biāo)準(zhǔn)重新設(shè)計(jì)一下 Arraylist 類。

class Arraylist<E> {
    private Object[] elementData;
    private int size = 0;

    public Arraylist(int initialCapacity) {
        this.elementData = new Object[initialCapacity];
    }
    
    public boolean add(E e) {
        elementData[size++] = e;
        return true;
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
}

一個(gè)泛型類就是具有一個(gè)或多個(gè)類型變量的類。Arraylist 類引入的類型變量為 E(Element,元素的首字母),使用尖括號(hào) <> 括起來(lái),放在類名的后面。

然后,我們可以用具體的類型(比如字符串)替換類型變量來(lái)實(shí)例化泛型類。

Arraylist list = new Arraylist();
list.add("沉默王三");
String str = list.get(0);

Date 類型也可以的。

Arraylist list = new Arraylist();
list.add(new Date());
Date date = list.get(0);

其次,我們還可以在一個(gè)非泛型的類(或者泛型類)中定義泛型方法。

class Arraylist<E> {
    public  T[] toArray(T[] a) {
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    }
}

不過(guò),說(shuō)實(shí)話,泛型方法的定義看起來(lái)略顯晦澀。來(lái)一副圖吧(注意:方法返回類型和方法參數(shù)類型至少需要一個(gè))。

現(xiàn)在,我們來(lái)調(diào)用一下泛型方法。

Arraylist list = new Arraylist<>(4);
list.add("沉");
list.add("默");
list.add("王");
list.add("二");

String [] strs = new String [4];
strs = list.toArray(strs);

for (String str : strs) {
    System.out.println(str);
}

最后,我們?cè)賮?lái)說(shuō)說(shuō)泛型變量的限定符 extends。在解釋這個(gè)限定符之前,我們假設(shè)有三個(gè)類,它們之間的定義是這樣的。

class Wanglaoer {
    public String toString() {
        return "王老二";
    }
}

class Wanger extends Wanglaoer{
    public String toString() {
        return "王二";
    }
}

class Wangxiaoer extends Wanger{
    public String toString() {
        return "王小二";
    }
}

我們使用限定符 extends 來(lái)重新設(shè)計(jì)一下 Arraylist 類。

class Arraylist<E extends Wanger> {
}

當(dāng)我們向 Arraylist 中添加 Wanglaoer 元素的時(shí)候,編譯器會(huì)提示錯(cuò)誤:Arraylist 只允許添加 Wanger 及其子類 Wangxiaoer 對(duì)象,不允許添加其父類 Wanglaoer

Arraylist list = new Arraylist<>(3);
list.add(new Wanger());
list.add(new Wanglaoer());
// The method add(Wanger) in the type Arraylist is not applicable for the arguments 
// (Wanglaoer)
list.add(new Wangxiaoer());

也就是說(shuō),限定符 extends 可以縮小泛型的類型范圍。

03、二哥,聽說(shuō)虛擬機(jī)沒(méi)有泛型?

三妹,你功課做得可以啊,連虛擬機(jī)都知道了啊。哥可以肯定地回答你,虛擬機(jī)是沒(méi)有泛型的。

啰嗦一句哈。我們編寫的 Java 代碼(也就是源碼,后綴為 .java 的文件)是不能夠被操作系統(tǒng)直接識(shí)別的,需要先編譯,生成 .class 文件(也就是字節(jié)碼文件)。然后 Java 虛擬機(jī)(JVM)會(huì)充當(dāng)一個(gè)翻譯官的角色,把字節(jié)碼翻譯給操作系統(tǒng)能聽得懂的語(yǔ)言,告訴它該干嘛。

怎么確定虛擬機(jī)沒(méi)有泛型呢?我們需要把泛型類的字節(jié)碼進(jìn)行反編譯——強(qiáng)烈推薦超神反編譯工具 Jad !

現(xiàn)在,在命令行中敲以下代碼吧(反編譯 Arraylist 的字節(jié)碼文件 Arraylist.class)。

jad Arraylist.class

命令執(zhí)行完后,會(huì)生成一個(gè) Arraylist.jad 的文件,用文本編輯工具打開后的結(jié)果如下。

// 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:   Arraylist.java

package com.cmower.java_demo.fanxing;

import java.util.Arrays;

class Arraylist
{

    public Arraylist(int initialCapacity)
    {
        size = 0;
        elementData = new Object[initialCapacity];
    }

    public boolean add(Object e)
    {
        elementData[size++] = e;
        return true;
    }

    Object elementData(int index)
    {
        return elementData[index];
    }

    private Object elementData[];
    private int size;
}

類型變量 消失了,取而代之的是 Object !

既然如此,那如果泛型類使用了限定符 extends,結(jié)果會(huì)怎么樣呢?我們先來(lái)看看 Arraylist2 的源碼。

class Arraylist2<E extends Wanger> {
    private Object[] elementData;
    private int size = 0;

    public Arraylist2(int initialCapacity) {
        this.elementData = new Object[initialCapacity];
    }

    public boolean add(E e) {
        elementData[size++] = e;
        return true;
    }

    E elementData(int index) {
        return (E) elementData[index];
    }
}

字節(jié)碼文件 Arraylist2.class 使用 Jad 反編譯后的結(jié)果如下。

// 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:   Arraylist2.java

package com.cmower.java_demo.fanxing;


// Referenced classes of package com.cmower.java_demo.fanxing:
//            Wanger

class Arraylist2
{

    public Arraylist2(int initialCapacity)
    {
        size = 0;
        elementData = new Object[initialCapacity];
    }

    public boolean add(Wanger e)
    {
        elementData[size++] = e;
        return true;
    }

    Wanger elementData(int index)
    {
        return (Wanger)elementData[index];
    }

    private Object elementData[];
    private int size;
}

類型變量 不見了,E 被替換成了 Wanger

通過(guò)以上兩個(gè)例子說(shuō)明,Java 虛擬機(jī)會(huì)將泛型的類型變量擦除,并替換為限定類型(沒(méi)有限定的話,就用 Object)。

04、二哥,類型擦除會(huì)有什么問(wèn)題嗎?

三妹啊,你還別說(shuō),類型擦除真的會(huì)有一些“問(wèn)題”。

我們來(lái)看一下這段代碼。

public class Cmower {
    
    public static void method(Arraylist list) {
        System.out.println("Arraylist list");
    }

    public static void method(Arraylist list) {
        System.out.println("Arraylist list");
    }

}

在淺層的意識(shí)上,我們會(huì)想當(dāng)然地認(rèn)為 Arraylist listArraylist list 是兩種不同的類型,因?yàn)?String 和 Date 是不同的類。

但由于類型擦除的原因,以上代碼是不會(huì)通過(guò)編譯的——編譯器會(huì)提示一個(gè)錯(cuò)誤(這正是類型擦除引發(fā)的那些“問(wèn)題”):

Erasure of method method(Arraylist) is the same as another method in type Cmower

Erasure of method method(Arraylist) is the same as another method in type Cmower

大致的意思就是,這兩個(gè)方法的參數(shù)類型在擦除后是相同的。

也就是說(shuō),method(Arraylist list)method(Arraylist list) 是同一種參數(shù)類型的方法,不能同時(shí)存在。類型變量 StringDate 在擦除后會(huì)自動(dòng)消失,method 方法的實(shí)際參數(shù)是 Arraylist list

有句俗話叫做:“百聞不如一見”,但即使見到了也未必為真——泛型的擦除問(wèn)題就可以很好地佐證這個(gè)觀點(diǎn)。

05、二哥,聽說(shuō)泛型還有通配符?

三妹啊,哥突然覺(jué)得你很適合作一枚可愛(ài)的程序媛啊!你這預(yù)習(xí)的功課做得可真到家啊,連通配符都知道!

通配符使用英文的問(wèn)號(hào)(");extends 限定子類,也可以使用關(guān)鍵字 super 限定父類。

為了更好地解釋通配符,我們需要對(duì) Arraylist 進(jìn)行一些改進(jìn)。

class Arraylist<E> {
    private Object[] elementData;
    private int size = 0;

    public Arraylist(int initialCapacity) {
        this.elementData = new Object[initialCapacity];
    }

    public boolean add(E e) {
        elementData[size++] = e;
        return true;
    }

    public E get(int index) {
        return (E) elementData[index];
    }
    
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
    public String toString() {
        StringBuilder sb = new StringBuilder();
        
        for (Object o : elementData) {
            if (o != null) {
                E e = (E)o;
                sb.append(e.toString());
                sb.append(",").append(" ");
            }
        }
        return sb.toString();
    }

    public int size() {
        return size;
    }
    
    public E set(int index, E element) {
        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }
}

1)新增 indexOf(Object o) 方法,判斷元素在 Arraylist 中的位置。注意參數(shù)為 Object 而不是泛型 E

2)新增 contains(Object o) 方法,判斷元素是否在 Arraylist 中。注意參數(shù)為 Object 而不是泛型 E

3)新增 toString() 方法,方便對(duì) Arraylist 進(jìn)行打印。

4)新增 set(int index, E element) 方法,方便對(duì) Arraylist 元素的更改。

你知道,Arraylist list = new Arraylist(); 這樣的語(yǔ)句是無(wú)法通過(guò)編譯的,盡管 Wangxiaoer 是 Wanger 的子類。但如果我們確實(shí)需要這種 “向上轉(zhuǎn)型” 的關(guān)系,該怎么辦呢?這時(shí)候就需要通配符來(lái)發(fā)揮作用了。

利用 <"); 形式的通配符,可以實(shí)現(xiàn)泛型的向上轉(zhuǎn)型,來(lái)看例子。

Arraylist<");new Arraylist<>(4);
list2.add(null);
// list2.add(new Wanger());
// list2.add(new Wangxiaoer());

Wanger w2 = list2.get(0);
// Wangxiaoer w3 = list2.get(1);

list2 的類型是 Arraylist<");,翻譯一下就是,list2 是一個(gè) Arraylist,其類型是 Wanger 及其子類。

注意,“關(guān)鍵”來(lái)了!list2 并不允許通過(guò) add(E e) 方法向其添加 Wanger 或者 Wangxiaoer 的對(duì)象,唯一例外的是 null。為什么不能存呢?原因還有待探究(苦澀)。

那就奇了怪了,既然不讓存放元素,那要 Arraylist<"); 這樣的 list2 有什么用呢?

雖然不能通過(guò) add(E e) 方法往 list2 中添加元素,但可以給它賦值。

Arraylist list = new Arraylist<>(4);

Wanger wanger = new Wanger();
list.add(wanger);

Wangxiaoer wangxiaoer = new Wangxiaoer();
list.add(wangxiaoer);

Arraylist<");1);
System.out.println(w2);

System.out.println(list2.indexOf(wanger));
System.out.println(list2.contains(new Wangxiaoer()));

Arraylist<"); 語(yǔ)句把 list 的值賦予了 list2,此時(shí) list2 == list。由于 list2 不允許往其添加其他元素,所以此時(shí)它是安全的——我們可以從容地對(duì) list2 進(jìn)行 get()indexOf()contains()。想一想,如果可以向 list2 添加元素的話,這 3 個(gè)方法反而變得不太安全,它們的值可能就會(huì)變。

利用 <"); 形式的通配符,可以向 Arraylist 中存入父類是 Wanger 的元素,來(lái)看例子。

Arraylist<");super Wanger> list3 = new Arraylist<>(4);
list3.add(new Wanger());
list3.add(new Wangxiaoer());

// Wanger w3 = list3.get(0);

需要注意的是,無(wú)法從 Arraylist<"); 這樣類型的 list3 中取出數(shù)據(jù)。為什么不能取呢?原因還有待探究(再次苦澀)。

雖然原因有待探究,但結(jié)論是明確的:<"); 可以取數(shù)據(jù),<"); 可以存數(shù)據(jù)。那么利用這一點(diǎn),我們就可以實(shí)現(xiàn)數(shù)組的拷貝——<"); 作為源(保證源不會(huì)發(fā)生變化),<"); 作為目標(biāo)(可以保存值)。

public class Collections {
	public static  void copy(Arraylist<");super T> dest, Arraylist<"); {
		for (int i = 0; i < src.size(); i++)
			dest.set(i, src.get(i));
	}
}
06、故事的未完待續(xù)

“二哥,你今天苦澀了啊!嘿嘿。竟然還有你需要探究的。”三妹開始調(diào)皮了起來(lái)。

“......”

“不要不好意思嘛,等三妹啥時(shí)候探究出來(lái)了原因,三妹給你講,好不好?”三妹越說(shuō)越來(lái)勁了。

“......”

“二哥,你還在想泛型通配符的原因啊!那三妹先去預(yù)習(xí)下個(gè)知識(shí)點(diǎn)了啊,你思考完了,再給我講!”三妹看著我陷入了沉思,扔下這句話走了。

“......”

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/7392.html

相關(guān)文章

  • 可在JVM、nodejs和瀏覽器運(yùn)行的靜態(tài)編譯語(yǔ)言 Ceylon 發(fā)布 1.0 版

    摘要:是一個(gè)可運(yùn)行于和瀏覽器的靜態(tài)語(yǔ)言。近日發(fā)布了正式版本。語(yǔ)言包及工具此次發(fā)布的版本中包括完整的語(yǔ)言規(guī)范。命令行工具集,包括針對(duì)和的編譯器文檔編譯器。功能強(qiáng)大的模塊架構(gòu),可用于組織代碼,管理依賴,在運(yùn)行時(shí)隔離模塊。 Ceylon是一個(gè)可運(yùn)行于JVM、nodejs和瀏覽器的靜態(tài)OO語(yǔ)言。 showImg(http://segmentfault.com/img/bVbA0f); 近日Cey...

    mo0n1andin 評(píng)論0 收藏0
  • 聊聊Java泛型及實(shí)現(xiàn)

    摘要:靜態(tài)變量是被泛型類的所有實(shí)例所共享的。所以引用能完成泛型類型的檢查。對(duì)于這個(gè)類型系統(tǒng),有如下的一些規(guī)則相同類型參數(shù)的泛型類的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。事實(shí)上,泛型類擴(kuò)展都不合法。 前言 和C++以模板來(lái)實(shí)現(xiàn)靜多態(tài)不同,Java基于運(yùn)行時(shí)支持選擇了泛型,兩者的實(shí)現(xiàn)原理大相庭徑。C++可以支持基本類型作為模板參數(shù),Java卻只能接受類作為泛型參數(shù);Java可以在泛型類的方法中取得...

    lewif 評(píng)論0 收藏0
  • 初探Java類型擦除

    摘要:可以看到,如果我們給泛型類制定了上限,泛型擦除之后就會(huì)被替換成類型的上限。相應(yīng)的,泛型類中定義的方法的類型也是如此。參考語(yǔ)言類型擦除下界通配符和的區(qū)別 本篇博客主要介紹了Java類型擦除的定義,詳細(xì)的介紹了類型擦除在Java中所出現(xiàn)的場(chǎng)景。 1. 什么是類型擦除 為了讓你們快速的對(duì)類型擦除有一個(gè)印象,首先舉一個(gè)很簡(jiǎn)單也很經(jīng)典的例子。 // 指定泛型為String List list1 ...

    DevTalking 評(píng)論0 收藏0
  • 理解Java泛型(一)

    摘要:參數(shù)化的類型其中是參數(shù)化的類型。類型參數(shù)的實(shí)例或?qū)嶋H類型參數(shù)其中是類型參數(shù)的實(shí)例或?qū)嶋H類型參數(shù)。它們并沒(méi)有重載,而且泛型中也不存在重載這一說(shuō)法。除此之外,我們應(yīng)該盡量地多用泛型方法,而減少對(duì)整個(gè)類的泛化,因?yàn)榉盒头椒ǜ菀装咽虑檎f(shuō)明白。 泛型是適用于許多許多的類型 ---《JAVA編程思想》 在Java的面向?qū)ο缶幊踢^(guò)程中, 或許你知道運(yùn)用繼承、接口等一系列面向?qū)ο蟮膭?dòng)作來(lái)實(shí)現(xiàn)代碼復(fù)用...

    YFan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<