Ash*_*wal 5 java generics list
我必须定义一个List,它有两种可能的值
如何创建一个类型安全的List,因为它只接受这两种类型?
我想避免使用原始List.
我不会声称这是一个完美的解决方案,但我会建议你选择一个"持有人"级别 - 也被一些作家称为"标记类"(包括Joshua Bloch,他说标记了类是"冗长,容易出错,效率低下").
但是,鉴于你的情况,我看不到更好的方法.以下解决方案提供:
你可以像这样定义你的持有者类:
class Holder { private String foo; private UserClass bar; boolean isString; boolean initialized=false; Holder (String str) { foo = str; isString=true; } Holder (UserClass bar) { this.bar = bar; isString=false; } String getStringVal () { if (! initialized) throw new IllegalStateException ("not initialized yet"); if (! isString) throw new IllegalStateException ("contents not string"); return foo; } // with a similar method for getUserClassVal() ... }
另一种方法是使用enum
for作为标记,而不是boolean isString - 这具有易于扩展到其他类型的值.
那么当然你有你的化合物清单:
List samsList = new ArrayList()
插入很简单,并且根据您的要求,编译时类型安全:
samsList.add (new Holder(stringVal)); samsList.add (new Holder(userClassVal));
从列表中检索值只是稍微复杂一些:在决定使用哪个getter之前,必须检查标记(holder.isString()).例如,列表上的foreach迭代看起来像这样:
for (Holder holder: samsList) { if (holder.isString()) doYourStringProcessing (holder.getStringVal()); else doYourUserClassProcessing (holder.getUserClassVal()); }
就像我说的那样,我并不认为这是完美的,但它符合您的要求将满足您的需求并最大限度地减轻呼叫者的负担.
但是,我想指出,这对我来说感觉好像可能会考虑在某处重构/重新设计.我遵循的指导原则之一是,每当我发现自己为合理的练习辩护时,它应该比简单地"我怎么能这样做?"更值得思考.
原因如下:假设我认为异常在这种情况下是正确的,那么实际上只有两种可能性.一个是声音练习是不完整的(所以"首选X超过Y"应该改写为"除了Z以外的首选X").
但更有可能的是,潜在的条款是一个不完美的设计,我们应该努力做一些重新设计/重构.
由于String
是Object
最终的子类,并且是最终的,因此您不会在String和除用户定义的类之间找到常见的超类型Object
.所以List<Object>
你必须使用它.
从设计的角度来看,在一个集合中混合不相关的类是一个坏主意.想想你想要完成的事情,你可能会想出一个更好的方法.
我建议使用类似“任一”类型的东西。Either 可以保存一种类型或另一种类型的值,但不能同时保存两者。然后,您的列表将具有其中之一的类型。这是一个更明确的解决方案。
这是用法:
final List<Either<Integer, String>> list = new ArrayList<>();
list.add(Either.left(1234));
list.add(Either.right("hello"));
for (final Either<Integer, String> i : list) {
if (i.isLeft()) {
System.out.println(i.left());
} else {
System.out.println(i.right());
}
}
Run Code Online (Sandbox Code Playgroud)
这是代码:
package com.stackoverflow.q1351335;
public interface Either<L, R> {
boolean isLeft();
L left();
R right();
static <L, R> Either<L, R> left(final L value) {
return new EitherLeft<>(value);
}
static <L, R> Either<L, R> right(final R value) {
return new EitherRight<>(value);
}
}
Run Code Online (Sandbox Code Playgroud)
package com.stackoverflow.q1351335;
import java.util.Objects;
public final class EitherLeft<L, R> implements Either<L, R> {
private final L value;
public boolean isLeft() {
return true;
}
public L left() {
return value;
}
public R right() {
throw new IllegalStateException("Cannot get right value from an EitherLeft");
}
public EitherLeft(final L value) {
super();
this.value = value;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof EitherLeft) {
Objects.equals(this.value, ((EitherLeft) obj).value);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public String toString() {
return "EitherLeft[" + Objects.toString(value) + "]";
}
}
Run Code Online (Sandbox Code Playgroud)
package com.stackoverflow.q1351335;
import java.util.Objects;
public final class EitherRight<L, R> implements Either<L, R> {
private final R value;
public boolean isLeft() {
return false;
}
public L left() {
throw new IllegalStateException("Cannot get left value from an EitherRight");
}
public R right() { return value; }
public EitherRight(final R value) {
super();
this.value = value;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof EitherRight) {
Objects.equals(this.value, ((EitherRight) obj).value);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public String toString() {
return "EitherRight[" + Objects.toString(value) + "]";
}
}
Run Code Online (Sandbox Code Playgroud)
更新
对此代码的一个很好的改进是添加一个“match”函数Either
:
// Illustration of usage:
myEither.match(
leftValue -> {
// Do stuff with left
},
rightValue -> {
// Do stuff with right
});
Run Code Online (Sandbox Code Playgroud)
这种方法的优点是不可能意外访问 an 的左侧EitherRight
或 an 的右侧EitherLeft
。这是对更多函数式(如 FP)语言所使用方法的模仿。
我想定义一个列表,它只能接受两种类型的对象(上面定义的),除了这两种类型之外的任何添加尝试都会提示我“编译时间错误”
这符合您的要求(有运行时错误):
public class MyList extends ArrayList<Object> {
public MyList() {
super();
}
public MyList(int initialSize) {
super(initialSize);
}
@Override
public void add(Object obj) {
if ((obj instanceof String) || (obj instanceof SomeType)) {
add(obj);
} else {
throw new IllegalArgumentException("not a String or SomeType");
}
}
public void add(String s) {
super.add(s);
}
public void add(SomeType s) {
super.add(s);
}
}
Run Code Online (Sandbox Code Playgroud)
在 Java 中没有办法实现这一点,这会给您带来编译时错误,即您将错误类型(在您的意义上)的元素添加到List
. 但是,如果这不是List
,您可以将类定义为具有重载add
方法等。创建新的“adder”方法不会对您有帮助,因为现有add(T)
方法仍然存在于界面中。无论你做什么(在Java中),调用它都不会出现编译时错误。
归档时间: |
|
查看次数: |
8884 次 |
最近记录: |