【问题标题】:Creating own Java Immutable Persistent List - typing issues创建自己的 Java 不可变持久列表 - 输入问题
【发布时间】:2014-07-05 22:30:37
【问题描述】:

我正在尝试学习如何创建一个不可变的持久列表。现在我的实现是在 java 中,虽然我对这个概念更感兴趣,并弄清楚如何很好地使用静态类型。

我的第一个实现是有效的。它有final int sizefinal T headfinal PersistentList<T> tail 字段。它只是一个单向链表,每个节点指向另一个相同PersistentList<T> 类型的节点(通过tail 字段),或者在链表末尾的情况下指向null。这就是问题所在,我不想到处检查null,也不想使用null 来表示列表结尾/空列表。我想用一个新的EmptyList 对象来表示一个空列表,它总是 是我的 PersistentList 抽象类中的最后一个节点。这就是 clojure doesEric Lippert demonstrated(在 C# 中)。

我的问题是,由于这个新的 EmptyList 对象属于不同的类型,我在在正确的位置指定正确的类型时遇到问题,编译器会抱怨。

我创建了一个抽象类IPersistentList,并从中派生了PersistentListEmptyList。目标是具有以下列表结构:

|PersistentList<T>| --> |PersistentList<T>| --> |EmptyList<T>|

现在像tail 或静态create 函数这样的方法需要能够返回PersistentList&lt;T&gt;EmptyList&lt;T&gt; 实例,具体取决于它是否是列表中的最后一个节点,但我该如何指定正确的返回类型?我的猜测是总是返回父抽象类的类型IPersistentList&lt;T&gt;。但这要求我总是强制转换派生类,这感觉非常肮脏,编译器仍在抱怨。我也不想要需要客户端代码来初始化类型列表然后必须强制转换的解决方案。我希望它是透明的。

这是我的代码的gist。这是错误:

persistent_list/src/main/java/com/benreinhart/persistentlist/PersistentList.java:15: error: constructor PersistentList in class PersistentList<T#2> cannot be applied to given types;
    PersistentList<T> p = new PersistentList<T>(head, PersistentList.empty(), 1);
                          ^
  required: T#1,IPersistentList<T#1>,int
  found: T#1,IPersistentList<Object>,int
  reason: actual argument IPersistentList<Object> cannot be converted to IPersistentList<T#1> by method invocation conversion
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>create(T#1)
    T#2 extends Object declared in class PersistentList
Note: persistent_list/src/main/java/com/benreinhart/persistentlist/PersistentList.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

如您所见,编译器抱怨我尝试将 EmptyList&lt;T&gt; 转换为 IPersistentList&lt;T&gt;

一般来说,在给定静态类型约束的情况下,我将如何做这样的事情?无论如何,如果是这样,我可以在不到处铸造的情况下做到这一点吗?我尽力遵循其数据结构的 clojure 源代码,但那里还有很多其他东西,而且我对静态类型(和 java)的语言很陌生,所以我无法掌握所有它。

提前致谢!

【问题讨论】:

    标签: java list persistent typing


    【解决方案1】:

    问题是Java 无法推断empty() 方法的类型参数。它放弃并假设它是ObjectEmptyList&lt;Object&gt; 不能转换为 IPersistentList&lt;T&gt;

    要解决这个问题,您可以显式传递它:

    PersistentList<T> p = new PersistentList<T>(head, PersistentList.<T>empty(), 1);
    

    提取empty() 到临时局部变量:

    IPersistentList<T> empty = PersistentList.empty();
    PersistentList<T> p = new PersistentList<T>(head, empty, 1);
    

    或者使用Java 8,应该可以处理这种情况。

    更多详情请看这里:http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types

    【讨论】:

    • Java 8 还安全吗?我读到他们匆忙推出并承认他们没有时间检查它是否正常/安全。
    • 谢谢!您的解决方案修复了错误,但现在它警告我有关未经检查的演员表。我收到此错误(来自empty() 方法):persistent_list/src/main/java/com/benreinhart/persistentlist/PersistentList.java:11: 警告:[unchecked] unchecked cast return (IPersistentList) EMPTY; ^ 必需:IPersistentList 找到:EmptyList,其中 T 是类型变量:T 扩展方法中声明的对象 empty() 1 警告
    • 有没有办法让编译器认为代码没问题(不关闭警告)?另外,有没有什么方法可以完成我想要做的事情而不必在任何地方强制转换类型?任何提示,想法?谢谢
    • 我想现在我会添加“@SuppressWarnings("unchecked")”,但如果有一种方法可以构建我的列表而不必不断地转换,我会更喜欢。看起来这似乎是不可能的。
    • 没有办法告诉(IPersistentList&lt;T&gt;) EMPTY 是安全的。忽略这种情况或返回新实例:new EmptyList&lt;T&gt;()。不需要其他演员表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    • 1970-01-01
    • 2013-11-24
    • 2020-05-27
    • 2019-02-26
    • 2015-08-01
    相关资源
    最近更新 更多