亚型多态性应用于元组的危险
Java 8具有l(wèi)ambda和stream,但是沒有元組,這真是令人遺憾 。 這就是為什么我們?cè)趈OOλ中實(shí)現(xiàn)了元組-Java 8缺少的部分 。 元組確實(shí)是無聊的值類型容器。 本質(zhì)上,它們只是這些類型的枚舉:
public class Tuple2<T1, T2> {public final T1 v1;public final T2 v2;public Tuple2(T1 v1, T2 v2) {this.v1 = v1;this.v2 = v2;}// [...] }public class Tuple3<T1, T2, T3> {public final T1 v1;public final T2 v2;public final T3 v3;public Tuple3(T1 v1, T2 v2, T3 v3) {this.v1 = v1;this.v2 = v2;this.v3 = v3;}// [...] }編寫元組類是一項(xiàng)非常無聊的任務(wù),最好使用源代碼生成器來完成。
其他語言和API的元組
jOOλ的當(dāng)前版本具有0至16度的元組。C#和其他.NET語言的元組類型介于1至8之間。有一個(gè)專門針對(duì)元組的特殊庫,稱為Javatuple ,元組在1至10之間。英里,并給元組單獨(dú)的英文名稱:
Unit<A> // (1 element) Pair<A,B> // (2 elements) Triplet<A,B,C> // (3 elements) Quartet<A,B,C,D> // (4 elements) Quintet<A,B,C,D,E> // (5 elements) Sextet<A,B,C,D,E,F> // (6 elements) Septet<A,B,C,D,E,F,G> // (7 elements) Octet<A,B,C,D,E,F,G,H> // (8 elements) Ennead<A,B,C,D,E,F,G,H,I> // (9 elements) Decade<A,B,C,D,E,F,G,H,I,J> // (10 elements)為什么?
因?yàn)楫?dāng)我看到恩納德時(shí),它真的會(huì)敲響那甜蜜的鐘聲
最后但并非最不重要的一點(diǎn)是,jOOQ還具有一個(gè)類似于元組的內(nèi)置類型org.jooq.Record ,它是Record7<T1, T2, T3, T4, T5, T6, T7>等漂亮子類型的基本類型Record7<T1, T2, T3, T4, T5, T6, T7> 。 jOOQ遵循Scala并定義了最高22級(jí)的記錄。
定義元組類型層次結(jié)構(gòu)時(shí)要當(dāng)心
正如我們?cè)谇懊娴氖纠锌吹降?#xff0c; Tuple3與Tuple2有很多共同的代碼。
由于數(shù)十年來的對(duì)象定向和多態(tài)設(shè)計(jì)反模式對(duì)我們所有人都造成了嚴(yán)重的大腦損害,我們可能認(rèn)為讓Tuple3<T1, T2, T3>擴(kuò)展Tuple2<T1, T2>是一個(gè)好主意,因?yàn)門uple3只是在Tuple2的右邊添加了一個(gè)屬性,對(duì)嗎? 所以…
public class Tuple3<T1, T2, T3> extends Tuple2<T1, T2> {public final T3 v3;public Tuple3(T1 v1, T2 v2, T3 v3) {super(v1, v2);this.v3 = v3;}// [...] }事實(shí)是:由于種種原因,這是您最糟糕的事情。 首先,是的。 Tuple2和Tuple3都是元組,因此它們確實(shí)具有一些共同的特征。 將這些功能歸為一個(gè)普通的超級(jí)類型并不是一個(gè)壞主意,例如:
public class Tuple2<T1, T2> implements Tuple {// [...] }但是學(xué)位不是其中之一。 原因如下:
排列
考慮一下您可以形成的所有可能的元組。 如果讓元組彼此延伸,那么例如, Tuple5也將與Tuple2分配兼容。 以下將完美地編譯:
Tuple2<String, Integer> t2 = tuple("A", 1, 2, 3, "B");當(dāng)讓Tuple3擴(kuò)展Tuple2 ,從擴(kuò)展鏈中的元組中刪除最右邊的屬性似乎是一個(gè)不錯(cuò)的默認(rèn)選擇。
但是在上面的示例中,為什么我不想重新分配(v2, v4)以使結(jié)果為(1, 3)或也許是(v1, v3)從而使結(jié)果為("A", 2) ?
當(dāng)將較高程度的元組“減少”到較低程度的元組時(shí),可能會(huì)涉及很多可能的屬性。 默認(rèn)情況下,刪除最右邊的屬性對(duì)于所有用例來說都不會(huì)足夠普遍
類型系統(tǒng)
如果Tuple3擴(kuò)展了Tuple2 ,則對(duì)類型系統(tǒng)的影響將遠(yuǎn)遠(yuǎn)大于上述Tuple2 。 例如,簽出jOOQ API。 在jOOQ中,您可以放心地假設(shè)以下內(nèi)容 :
// Compiles: TABLE1.COL1.in(select(TABLE2.COL1).from(TABLE2))// Must not compile: TABLE1.COL1.in(select(TABLE2.COL1, TABLE2.COL2).from(TABLE2))第一個(gè)IN謂詞是正確的。 謂詞的左側(cè)只有一列( 而不是行值表達(dá)式 )。 這意味著謂詞的右側(cè)也必須對(duì)單列表達(dá)式進(jìn)行操作,例如,選擇單個(gè)列(相同類型)的SELECT子查詢。
第二個(gè)示例選擇了太多列,并且jOOQ API將告訴Java編譯器這是錯(cuò)誤的。
jOOQ通過Field.in(Select)方法保證了這一點(diǎn),該方法的簽名為:
public interface Field<T> {...Condition in(Select<? extends Record1<T>> select);... }因此,您可以提供一個(gè)產(chǎn)生Record1<T>類型的任何子類型的SELECT語句。
幸運(yùn)的是, Record2不會(huì)擴(kuò)展Record1
如果現(xiàn)在Record2擴(kuò)展了Record1 , Record1似乎是個(gè)好主意,那么第二個(gè)查詢將突然編譯:
// This would now compile TABLE1.COL1.in(select(TABLE2.COL1, TABLE2.COL2).from(TABLE2))…即使它形成無效的SQL語句。 它將進(jìn)行編譯,因?yàn)樗鼘⑸蒘elect<Record2<Type1, Type2>>類型,該類型將是Field.in(Select)方法中預(yù)期的Select<Record1<Type1>>的子類型。
結(jié)論
Tuple2和Tuple5類型基本上是不兼容的類型。 在強(qiáng)類型系統(tǒng)中,您一定不要引誘類似類型或相關(guān)類型也應(yīng)該是兼容類型。
類型層次結(jié)構(gòu)是非常面向?qū)ο蟮?#xff0c;從面向?qū)ο蟮慕嵌葋砜?#xff0c;我的意思是自90年代以來我們?nèi)匀辉馐苤腥毕莺瓦^度設(shè)計(jì)的面向?qū)ο蟾拍睢?即使在“企業(yè)”中,大多數(shù)人也學(xué)會(huì)了偏重于繼承而不是繼承 。 對(duì)于元組,組合意味著您可以很好地將 Tuple5轉(zhuǎn)換為Tuple2 。 但是您不能分配它。
在jOOλ中 ,可以很容易地完成以下轉(zhuǎn)換:
// Produces (1, 3) Tuple2<String, Integer> t2_4 = tuple("A", 1, 2, 3, "B").map((v1, v2, v3, v4, v5) -> tuple(v2, v4));// Produces ("A", 2) Tuple2<String, Integer> t1_3 = tuple("A", 1, 2, 3, "B").map((v1, v2, v3, v4, v5) -> tuple(v1, v3));這個(gè)想法是您對(duì)不可變值進(jìn)行操作,并且可以輕松提取這些值的一部分并將其映射/重組為新值。
翻譯自: https://www.javacodegeeks.com/2015/10/the-danger-of-subtype-polymorphism-applied-to-tuples.html
總結(jié)
以上是生活随笔為你收集整理的亚型多态性应用于元组的危险的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 济南备案查询系统(济南备案查询)
- 下一篇: ddos的攻击方式(ddos攻击方式总结