java中的xxxable和xxxator使用及說(shuō)明
前言
相信有一定工作經(jīng)驗(yàn)的朋友,都見(jiàn)過(guò)或者用過(guò)xxxable和xxxator ,比如常見(jiàn)的Comparable和Comparator, 還有還有常見(jiàn)并且容易迷糊的Iterable和Iterator, 看這名字,前兩個(gè)是和比較相關(guān)的, 后兩個(gè)是和迭代相關(guān). 但是命名如此相似的接口, 又有何區(qū)別呢?各自的用途又是什么呢? 今天阿亮帶大家一起揭開這神秘的面紗.
揭秘
首先我們要明確的是,xxxable和xxxator都是接口, 都是用來(lái)描述其子類具有某種能力的. 因?yàn)樵趈ava中,接口就是用來(lái)定義能力的. 接口中的方法,就代表著其實(shí)現(xiàn)類有什么能力. 因?yàn)閷?shí)現(xiàn)類必須要實(shí)現(xiàn)接口中的所有方法.
從名稱上感受
首先,直觀的從名稱上直觀的感受,復(fù)習(xí)一下英文
- –able: 表形容詞, 可…的,能…
所以,今天我們要研究的其中兩個(gè)主角,
- Comparable: 可比較的Iterable: 可迭代的
- -ator: 表名詞,通常由ate結(jié)尾的動(dòng)詞而來(lái), 做事的人或物
所以,我們今天要研究的另外兩個(gè)主角
- Comparator: 比較器
- Iterator: 迭代器
一番咬文嚼字, 從字面上理解:
xxxable 就是具有xxx能力,是形容詞,帶入java接口,就這么認(rèn)為: 實(shí)現(xiàn)了xxxable接口,代表著具有了xxx能力,可以進(jìn)行xxx. 著重描述的是: 實(shí)現(xiàn)類具有xxx能力.
xxxator 就是xxx器,是名詞, 帶入java中的接口,就可以這么認(rèn)為: 實(shí)現(xiàn)了xxxator接口,就代表著可以干xx. 著重描述的是: 實(shí)現(xiàn)類可以對(duì)某個(gè)對(duì)象進(jìn)行xxx.
看到這兒,可能還是有點(diǎn)模糊,不用著急,我們從代碼,親自來(lái)感受一下,感受完了再回過(guò)頭來(lái)看這段.
從代碼帶入
上面我們從名稱上理解了,下面我們從實(shí)操代碼入手,直觀感受.
Comparable接口
位于java.lang 包下,只有一個(gè)抽象方法
public int compareTo(T o);
實(shí)現(xiàn)這個(gè)方法,必須實(shí)現(xiàn)compareTo這個(gè)方法,實(shí)現(xiàn)類就具有可比較的能力了.
比較肯定要區(qū)分大小,那這個(gè)方法要怎么區(qū)分大小呢? 方法的doc注釋上解釋了
a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
大意就是 返回一個(gè)負(fù)整數(shù),零或正整數(shù),表示此對(duì)象小于,等于或大于指定的對(duì)象。
現(xiàn)在我們假設(shè)一個(gè)場(chǎng)景,我們有一個(gè)<書本>對(duì)象,有一個(gè)序號(hào)屬性,通過(guò)序號(hào)的大小,來(lái)對(duì)書進(jìn)行比較,一遍排序整理.指定的規(guī)則是: 編號(hào)小的更大,編號(hào)大的更小,
書籍類的聲明如下:
public class Book implements Comparable<Book>{ private int order; private String name; public Book(int order, String name) { this.order = order; this.name = name; } public int getOrder() { return order; } @Override public int compareTo(Book o) { // 相等 if (this == o || this.getOrder() == o.getOrder()) return 0; // 如果 此類的order大于被比較的order,返回-1,表示此書"更小" if (this.order > o.getOrder()) { return -1; } return 1; } @Override public String toString() { return "Book{" + "order=" + order + ", name='" + name + '\'' + '}'; }
怎么使用呢, 最簡(jiǎn)單的是比較兩本書
// 比較兩本書 Book songs = new Book(1, "詩(shī)經(jīng)"); Book threeKingdoms = new Book(2, "三國(guó)演義"); int i = songs.compareTo(threeKingdoms); // 返回1, 正數(shù), 代表 詩(shī)經(jīng) "大于" 三國(guó)演義 System.out.println(i);
感覺(jué)這樣好像沒(méi)有什么用. 下面展示一個(gè)比較可能會(huì)在開發(fā)中用到的: 對(duì)書籍?dāng)?shù)組進(jìn)行排序
// 對(duì)書進(jìn)行排序 Book[] books = new Book[3]; Book songs = new Book(1, "詩(shī)經(jīng)"); Book threeKingdoms = new Book(2, "三國(guó)演義"); Book soulLand = new Book(3, "斗羅大陸"); books[0] = soulLand; books[1] = songs; books[2] = threeKingdoms; // 排序之前 System.out.println(Arrays.toString(books)); // 利用Arrays.sort進(jìn)行排序 (這個(gè)方法是升序) Arrays.sort(books); // 排序之后 System.out.println(Arrays.toString(books));
輸出結(jié)果為:
[Book{order=3, name='斗羅大陸'}, Book{order=1, name='詩(shī)經(jīng)'}, Book{order=2, name='三國(guó)演義'}]
[Book{order=3, name='斗羅大陸'}, Book{order=2, name='三國(guó)演義'}, Book{order=1, name='詩(shī)經(jīng)'}]
因?yàn)槲覀兌x是: 編號(hào)越大,書籍 “越小”,所以這個(gè)結(jié)果是沒(méi)問(wèn)題. Arrays.sort方法就是利用Comparable,進(jìn)行比較然后排序的
如果Book這個(gè)類不實(shí)現(xiàn)java.lang.Comparable,然后調(diào)用Arrays.sort(books);猜猜會(huì)怎樣?
同理, java集合Stream流中的sorted()方法,也是同樣的道理. 此處不展開, 感興趣的朋友請(qǐng)移步: java stream使用指南-------sorted使用及進(jìn)階
另外,我們知道 java.util.TreeSet 這個(gè)集合,添加進(jìn)去的元素自動(dòng)就排序好了, 比如我 new TreeSet<Integer> ,然后往里面添加幾個(gè)數(shù)字,打印出來(lái)就是有序的, 或者new TreeSet<String>.
@Test public void test4(){ // 數(shù)字TreeSet TreeSet<Integer> integers = new TreeSet<>(); integers.add(20); integers.add(11); integers.add(34); integers.add(49); System.out.println(integers); // 字符串TreeSet TreeSet<String> strings = new TreeSet<>(); strings.add("B"); strings.add("E"); strings.add("G"); strings.add("A"); System.out.println(strings); }
輸出:
[11, 20, 34, 49]
[A, B, E, G]
我添加順序是隨意的, 最終打印出來(lái)的結(jié)果是有序的, 大家有沒(méi)有想過(guò)這個(gè)排序規(guī)則是怎么定義的能?在哪里定義的呢?
相信大家已經(jīng)猜到了, 其實(shí)就是Integer String已經(jīng)實(shí)現(xiàn)了java.lang.Comparable接口, TreeSet才知道他們的比較規(guī)則,然后由此來(lái)進(jìn)行排序. 如果你往TreeSet中添加一個(gè)沒(méi)有實(shí)現(xiàn)Comparable接口的元素, 看看會(huì)出現(xiàn)什么情況.
當(dāng)然,TreeSet還有另一種指定規(guī)則的方式,我們下面討論java.util.Comparator的時(shí)候再說(shuō)
Comparator 接口
說(shuō)完了Comparable,然后我們來(lái)說(shuō)說(shuō)Comparator
Comparator 位于java.util包下, 可以翻譯為比較器, 是一個(gè)函數(shù)式接口,其中只有一個(gè)抽象方法
int compare(T o1, T o2);
實(shí)現(xiàn)這個(gè)方法之后, 就可以對(duì)兩個(gè)對(duì)象進(jìn)行比較了. 因?yàn)镃omparator是比較器,是工具, 所以可以用這個(gè)工具來(lái)對(duì)兩個(gè)對(duì)象進(jìn)行比較
比較的規(guī)則也是類似, 如果compare方法返回了 了一個(gè)正數(shù) 0 負(fù)數(shù) ,則說(shuō)明 o1 大于 等于 小于 o2
上代碼 ,還是以上面的書籍為例, 但是不再實(shí)現(xiàn)Comparable接口
書籍類:
public class Book { private int order; private String name; public Book(int order, String name) { this.order = order; this.name = name; } public int getOrder() { return order; } @Override public String toString() { return "Book{" + "order=" + order + ", name='" + name + '\'' + '}'; } }
然后我們?cè)贋闀ㄒ粋€(gè)比較器, 書籍比較器
public class BookComparator implements Comparator<Book> { @Override public int compare(Book o1, Book o2) { if (o1 == o2 || o1.getOrder() == o2.getOrder()) return 0; if (o1.getOrder() > o2.getOrder()) { return -1; } return 1; } }
我們還是先比較兩本書,但是此時(shí)Book已經(jīng)沒(méi)有實(shí)現(xiàn)Comparable接口, 沒(méi)有compareTo方法,所以不能直接比較,需要使用 書籍比較器 進(jìn)行比較
// 比較兩本書 Book songs = new Book(1, "詩(shī)經(jīng)"); Book threeKingdoms = new Book(2, "三國(guó)演義"); // 使用書籍比較器進(jìn)行比較 BookComparator bookComparator = new BookComparator(); int i = bookComparator.compare(songs, threeKingdoms); // 返回1, 正數(shù), 代表 詩(shī)經(jīng) "大于" 三國(guó)演義 System.out.println(i);
再來(lái)排序,
// 對(duì)書進(jìn)行排序 Book[] books = new Book[3]; Book songs = new Book(1, "詩(shī)經(jīng)"); Book threeKingdoms = new Book(2, "三國(guó)演義"); Book soulLand = new Book(3, "斗羅大陸"); books[0] = soulLand; books[1] = songs; books[2] = threeKingdoms; // 排序之前 System.out.println(Arrays.toString(books)); // 利用Arrays.sort進(jìn)行排序,因?yàn)榇藭r(shí)Book沒(méi)有實(shí)現(xiàn)Comparable接口,直接使用下面的方法是沒(méi)辦法進(jìn)行排序的 // Arrays.sort(books); // 利用 書籍比較器進(jìn)行排序 BookComparator bookComparator = new BookComparator(); Arrays.sort(books,bookComparator); // 排序之后 System.out.println(Arrays.toString(books));
結(jié)果:
[Book{order=3, name='斗羅大陸'}, Book{order=1, name='詩(shī)經(jīng)'}, Book{order=2, name='三國(guó)演義'}]
[Book{order=3, name='斗羅大陸'}, Book{order=2, name='三國(guó)演義'}, Book{order=1, name='詩(shī)經(jīng)'}]
比較器畢竟是工具, 所以工具還提供了一些額外的方法比如reversed
上面的排序, 我看著不太舒服, 我想倒序一下, 當(dāng)然比較器已經(jīng)固定了,就不能再改了
在排序時(shí),可以這樣
Arrays.sort(books,bookComparator.reversed());
排序后的結(jié)果就是:
[Book{order=1, name='詩(shī)經(jīng)'}, Book{order=2, name='三國(guó)演義'}, Book{order=3, name='斗羅大陸'}]
同理, java集合Stream流中除了sorted()方法,還提供了一個(gè)重載方法 sorted(Comparator<? super T> comparator) 也是同樣的道理.傳入一個(gè)比較器進(jìn)行比較, 此處不展開, 感興趣的朋友請(qǐng)移步: java stream使用指南-------sorted使用及進(jìn)階
上面在講TreeSet時(shí),我們還留了一個(gè)懸念, TreeSet 可以通過(guò)另外一種方式指定排序規(guī)則, 那肯定是用 比較器來(lái)指定規(guī)則啦
首先我們使用前面的方式(注意:此時(shí)Book未實(shí)現(xiàn)Comparable接口)
// 我們先來(lái)試試之前我用的方法 TreeSet<Book> books = new TreeSet<>(); books.add(new Book(1, "詩(shī)經(jīng)")); books.add(new Book(3, "斗羅大陸")); books.add(new Book(2, "三國(guó)演義")); System.out.println(books);
肯定會(huì)出問(wèn)題的,因?yàn)榇藭r(shí)TreeSet已經(jīng)不知道用什么規(guī)則對(duì)添加進(jìn)來(lái)的元素排序了,此時(shí)應(yīng)該使用另一種方式
// new 書籍比較器 BookComparator bookComparator = new BookComparator(); // 構(gòu)造TreeSet時(shí)將比較器傳入 TreeSet<Book> books = new TreeSet<>(bookComparator); books.add(new Book(1, "詩(shī)經(jīng)")); books.add(new Book(3, "斗羅大陸")); books.add(new Book(2, "三國(guó)演義")); System.out.println(books);
結(jié)果也是一樣正確的 被排序好的.
這里多提一嘴, 因?yàn)镃omparator是個(gè)函數(shù)式接口, 更多時(shí)候我們使用的匿名內(nèi)部類或者lambda來(lái)實(shí)現(xiàn)一個(gè)構(gòu)造器,而不是單獨(dú)的創(chuàng)建一個(gè)書籍比較器, 除非這個(gè)比較器需要在不止一個(gè)地方使用,此處不展開描述.
好了到此為此, 我們已經(jīng)搞清楚了Comparable和Comparator的用法
總結(jié)
我們已經(jīng)知道Comparable和Comparator是怎么使用的了,回頭再去看 最初的字面描述,肯定有更深的理解.
我現(xiàn)在做個(gè)總結(jié): xxxable和xxxator 都是為做xxx事情而存在
- xxxable是個(gè)形容詞, 著重描述的是: 其實(shí)現(xiàn)類本身就具有xxx能力,
- xxxator是個(gè)名詞,意思是其實(shí)現(xiàn)類本身是xxx器, 一些不具備xxx能力的類可以借助xxx器有用xxx能力
面向?qū)ο螅?也是對(duì)現(xiàn)實(shí)世界的抽象, 我來(lái)舉個(gè)例子,以飛行(fly)舉例,
- Flyable: 可飛行的
- Flyator: 飛行器
一般的鳥類(Bird)都應(yīng)該實(shí)現(xiàn)Flyable接口,因?yàn)轼B類本身就具有飛行的能力
人類(Human)不能實(shí)現(xiàn)Flyable接口,因?yàn)槿祟惒痪哂酗w行的能力, 但是應(yīng)該存在 人類飛行器(HumanFlyator)這個(gè)類,這個(gè)類實(shí)現(xiàn)Flyator接口, 人類本身不具有飛行的能力,但是人類可以借助飛行器飛行.
還有Iterable和Iterator,也是一樣的,但是和 <比較> 不同的是, Iterable的迭代能力是借助Iterator完成的, 此處不在展開描述,感興趣的朋友可以自行研究.
不得不說(shuō),人家設(shè)計(jì)java語(yǔ)言的人,也是對(duì)現(xiàn)實(shí)世界有充分認(rèn)識(shí)的,這些命名是如此的深刻,內(nèi)涵. 我們?cè)谧约旱墓ぷ髦?,也可以參考,進(jìn)行規(guī)范的接口設(shè)計(jì)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud服務(wù)的發(fā)現(xiàn)與調(diào)用詳解
在Java微服務(wù)越來(lái)越火的今天。幾乎什么公司都在搞微服務(wù)。而使用的比較多的就是Spring?Cloud技術(shù)棧。今天就來(lái)研究一下Spring?Cloud中服務(wù)發(fā)現(xiàn)與調(diào)用的基本原理2022-07-07SpringBoot整個(gè)啟動(dòng)過(guò)程的分析
今天小編就為大家分享一篇關(guān)于SpringBoot整個(gè)啟動(dòng)過(guò)程的分析,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03java Tapestry4.1.2入門說(shuō)明教程
不必關(guān)心鏈接!不必關(guān)心請(qǐng)求(http request)到了哪里!不必關(guān)心響應(yīng)(http response)要轉(zhuǎn)向哪里!Tapestry構(gòu)建于底層的request-resonse模式,基于Servlet技術(shù),抽象出面向組件開發(fā)的模型。Tapestry關(guān)心的是:頁(yè)面、組件、事件、對(duì)象、方法、屬性!2008-11-11SpringBoot實(shí)現(xiàn)HTTP服務(wù)監(jiān)聽(tīng)的代碼示例
前后端分離項(xiàng)目中,在調(diào)用接口調(diào)試時(shí)候,我們可以通過(guò)cpolar內(nèi)網(wǎng)穿透將本地服務(wù)端接口模擬公共網(wǎng)絡(luò)環(huán)境遠(yuǎn)程調(diào)用調(diào)試,本次教程我們以Java服務(wù)端接口為例,需要的朋友可以參考下2023-05-05Java微信公眾平臺(tái)開發(fā)(3) 接收消息的分類及實(shí)體的創(chuàng)建
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開發(fā)第三步,接收消息的分類及實(shí)體的創(chuàng)建,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程
這篇文章主要介紹了springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12