对边做边学的赞誉。以下是改进“机会”的建议:
只有一种元组可以存在(一旦设置了Typelock)。这损害了想要使用多种类型的元组的程序的可重用性和可伸缩性,除非您诉诸剪切粘贴重用(BirthdayTuple,DimensionsTuple,StreetAddressTuple等)。考虑一个 TupleFactory 类,该类接受目标类型并创建一个元组生成器对象来生成元组。
“null”作为元组中值的有效性未记录在案。我认为在设置Typelock之前,允许空值;但是在设置Typelock之后,代码将生成一个NullPointerException - 这是不一致的。如果不允许它们,构造函数应捕获它并禁止它(无论 Typelock 如何)。如果允许它们,则整个代码(构造函数,等于,哈希码等)需要修改以允许它。
确定元组是否旨在成为不可变值对象。基于它缺乏设置方法,我猜是这样。如果是这样,那么要小心“采用”传入的数组 - 。即使它是一个 var arg 构造函数,也可以直接使用数组调用构造函数。该类采用数组(保留对它的引用),并且数组中的值可以在类外部进行更改。我会做一个数组的浅层副本,但也记录具有不可变值的元组的潜在问题(可以在元组之外更改)。lastTuple=this.arr
您的方法缺少空值检查 () 和类检查(或 )。平等的习语是有据可查的。equals
if (obj == null) return false
obj instanceof Tuple
this.getClass().equals(object.getClass())
除了通过 之外,没有办法查看元组的值。这保护了 的值和 整体不可变性,但我认为它限制了类的有用性。toString
虽然我意识到这只是一个例子,但我不希望将这个类用于生日/日期之类的东西。在具有固定对象类型的解决方案域中,实类(如 Date)要好得多。我认为这个类在元组是第一类对象的特定域中很有用。
编辑一直在想这个问题。以下是我对一些代码的看法(在github +测试上):
===
Tuple.java
===
package com.stackoverflow.tuple;
/**
* Tuple are immutable objects. Tuples should contain only immutable objects or
* objects that won't be modified while part of a tuple.
*/
public interface Tuple {
public TupleType getType();
public int size();
public <T> T getNthValue(int i);
}
===
TupleType.java
===
package com.stackoverflow.tuple;
/**
* Represents a type of tuple. Used to define a type of tuple and then
* create tuples of that type.
*/
public interface TupleType {
public int size();
public Class<?> getNthType(int i);
/**
* Tuple are immutable objects. Tuples should contain only immutable objects or
* objects that won't be modified while part of a tuple.
*
* @param values
* @return Tuple with the given values
* @throws IllegalArgumentException if the wrong # of arguments or incompatible tuple values are provided
*/
public Tuple createTuple(Object... values);
public class DefaultFactory {
public static TupleType create(final Class<?>... types) {
return new TupleTypeImpl(types);
}
}
}
===
TupleImpl.java (not visible outside package)
===
package com.stackoverflow.tuple;
import java.util.Arrays;
class TupleImpl implements Tuple {
private final TupleType type;
private final Object[] values;
TupleImpl(TupleType type, Object[] values) {
this.type = type;
if (values == null || values.length == 0) {
this.values = new Object[0];
} else {
this.values = new Object[values.length];
System.arraycopy(values, 0, this.values, 0, values.length);
}
}
@Override
public TupleType getType() {
return type;
}
@Override
public int size() {
return values.length;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getNthValue(int i) {
return (T) values[i];
}
@Override
public boolean equals(Object object) {
if (object == null) return false;
if (this == object) return true;
if (! (object instanceof Tuple)) return false;
final Tuple other = (Tuple) object;
if (other.size() != size()) return false;
final int size = size();
for (int i = 0; i < size; i++) {
final Object thisNthValue = getNthValue(i);
final Object otherNthValue = other.getNthValue(i);
if ((thisNthValue == null && otherNthValue != null) ||
(thisNthValue != null && ! thisNthValue.equals(otherNthValue))) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 17;
for (Object value : values) {
if (value != null) {
hash = hash * 37 + value.hashCode();
}
}
return hash;
}
@Override
public String toString() {
return Arrays.toString(values);
}
}
===
TupleTypeImpl.java (not visible outside package)
===
package com.stackoverflow.tuple;
class TupleTypeImpl implements TupleType {
final Class<?>[] types;
TupleTypeImpl(Class<?>[] types) {
this.types = (types != null ? types : new Class<?>[0]);
}
public int size() {
return types.length;
}
//WRONG
//public <T> Class<T> getNthType(int i)
//RIGHT - thanks Emil
public Class<?> getNthType(int i) {
return types[i];
}
public Tuple createTuple(Object... values) {
if ((values == null && types.length == 0) ||
(values != null && values.length != types.length)) {
throw new IllegalArgumentException(
"Expected "+types.length+" values, not "+
(values == null ? "(null)" : values.length) + " values");
}
if (values != null) {
for (int i = 0; i < types.length; i++) {
final Class<?> nthType = types[i];
final Object nthValue = values[i];
if (nthValue != null && ! nthType.isAssignableFrom(nthValue.getClass())) {
throw new IllegalArgumentException(
"Expected value #"+i+" ('"+
nthValue+"') of new Tuple to be "+
nthType+", not " +
(nthValue != null ? nthValue.getClass() : "(null type)"));
}
}
}
return new TupleImpl(this, values);
}
}
===
TupleExample.java
===
package com.stackoverflow.tupleexample;
import com.stackoverflow.tuple.Tuple;
import com.stackoverflow.tuple.TupleType;
public class TupleExample {
public static void main(String[] args) {
// This code probably should be part of a suite of unit tests
// instead of part of this a sample program
final TupleType tripletTupleType =
TupleType.DefaultFactory.create(
Number.class,
String.class,
Character.class);
final Tuple t1 = tripletTupleType.createTuple(1, "one", 'a');
final Tuple t2 = tripletTupleType.createTuple(2l, "two", 'b');
final Tuple t3 = tripletTupleType.createTuple(3f, "three", 'c');
final Tuple tnull = tripletTupleType.createTuple(null, "(null)", null);
System.out.println("t1 = " + t1);
System.out.println("t2 = " + t2);
System.out.println("t3 = " + t3);
System.out.println("tnull = " + tnull);
final TupleType emptyTupleType =
TupleType.DefaultFactory.create();
final Tuple tempty = emptyTupleType.createTuple();
System.out.println("\ntempty = " + tempty);
// Should cause an error
System.out.println("\nCreating tuple with wrong types: ");
try {
final Tuple terror = tripletTupleType.createTuple(1, 2, 3);
System.out.println("Creating this tuple should have failed: "+terror);
} catch (IllegalArgumentException ex) {
ex.printStackTrace(System.out);
}
// Should cause an error
System.out.println("\nCreating tuple with wrong # of arguments: ");
try {
final Tuple terror = emptyTupleType.createTuple(1);
System.out.println("Creating this tuple should have failed: "+terror);
} catch (IllegalArgumentException ex) {
ex.printStackTrace(System.out);
}
// Should cause an error
System.out.println("\nGetting value as wrong type: ");
try {
final Tuple t9 = tripletTupleType.createTuple(9, "nine", 'i');
final String verror = t9.getNthValue(0);
System.out.println("Getting this value should have failed: "+verror);
} catch (ClassCastException ex) {
ex.printStackTrace(System.out);
}
}
}
===
Sample Run
===
t1 = [1, one, a]
t2 = [2, two, b]
t3 = [3.0, three, c]
tnull = [null, (null), null]
tempty = []
Creating tuple with wrong types:
java.lang.IllegalArgumentException: Expected value #1 ('2') of new Tuple to be class java.lang.String, not class java.lang.Integer
at com.stackoverflow.tuple.TupleTypeImpl.createTuple(TupleTypeImpl.java:32)
at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.java:37)
Creating tuple with wrong # of arguments:
java.lang.IllegalArgumentException: Expected 0 values, not 1 values
at com.stackoverflow.tuple.TupleTypeImpl.createTuple(TupleTypeImpl.java:22)
at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.java:46)
Getting value as wrong type:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.stackoverflow.tupleexample.TupleExample.main(TupleExample.java:58)