سؤال
لقد صنعت للتو Java N-Tuple وهو نوع آمن.
أنا أستخدم بعض الأساليب غير التقليدية لتحقيق سلامة النوع (لقد صنعتها للتو).
هل يمكن لشخص ما إعطاء بعض المدخلات على تحسينه أو بعض العيوب الممكنة.
public class Tuple {
private Object[] arr;
private int size;
private static boolean TypeLock = false;
private static Object[] lastTuple = {1,1,1}; //default tuple type
private Tuple(Object ... c) {
// TODO Auto-generated constructor stub
size=c.length;
arr=c;
if(TypeLock)
{
if(c.length == lastTuple.length)
for(int i = 0; i<c.length; i++)
{
if(c[i].getClass() == lastTuple[i].getClass())
continue;
else
throw new RuntimeException("Type Locked");
}
else
throw new RuntimeException("Type Locked");
}
lastTuple = this.arr;
}
public static void setTypeLock(boolean typeLock) {
TypeLock = typeLock;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if (this == obj)
return true;
Tuple p = (Tuple)obj;
for (int i = 0; i < size; i++)
{
if (p.arr[i].getClass() == this.arr[i].getClass())
{
if (!this.arr[i].equals(p.arr[i]))
return false;
}
else
return false;
}
return true;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
int res = 17;
for(int i = 0; i < size; i++)
res = res*37+arr[i].hashCode();
return res;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return Arrays.toString(arr);
}
public static void main(String[] args) {
HashMap<Tuple,String> birthDay = new HashMap<Tuple,String>();
Tuple p = new Tuple(1,2,1986);
Tuple.setTypeLock(true);
Tuple p2 = new Tuple(2,10,2009);
Tuple p3 = new Tuple(1,2,2010);
Tuple p4 = new Tuple(1,2,2010);
birthDay.put(p,"Kevin");
birthDay.put(p2,"Smith");
birthDay.put(p3,"Sam");
birthDay.put(p4, "Jack");
System.out.println(birthDay);
System.out.println(birthDay.get(new Tuple(1,2,1986)));
birthDay.put(new Tuple(1,2,""),"");
}
}
المحلول
مجد على التعلم من خلال القيام به. فيما يلي اقتراحات "الفرص" للتحسين:
يمكن أن يوجد نوع واحد فقط من tuple (بمجرد تعيين typelock). هذا يؤلم قابلية إعادة الاستخدام وقابلية التوسع في البرامج التي ترغب في استخدام أنواع متعددة من tuples إلا إذا كنت تلجأ إلى إعادة استخدام الصغار (عيد ميلاد ، DimensionStuple ، StreetAddressTuple ، ...). فكر في فئة tuplefactory التي تقبل الأنواع المستهدفة وتنشئ كائن منشئ tuple لإنشاء tuples.
لم يتم توثيق صحة "NULL" كقيمة في tuple. أعتقد أنه قبل تعيين Typelock ، يُسمح بالرقم ؛ ولكن بعد تعيين Typelock ، سيقوم الكود بإنشاء nullpointerxception - وهذا غير متسق. إذا لم يُسمح لهم ، يجب على المُنشئ أن يمسك به وعدم السماح به (بغض النظر عن Typelock). إذا تم السماح بها ، فإن الكود بشكل عام (مُنشئ ، يساوي ، هاش كود ، إلخ) يحتاج إلى تعديل للسماح بذلك.
قرر ما إذا كان المقصود أن تكون كائنات قيمة غير قابلة للتغيير. بناءً على افتقارها إلى أساليب Setter ، أعتقد ذلك. إذا كان الأمر كذلك ، فاحرص على "تبني" الصفيف الوارد -
lastTuple=this.arr
. على الرغم من أنه مُنشئ Var Arg ، يمكن استدعاء المُنشئ مع صفيف مباشرة. يتبنى الفصل الصفيف (يحافظ على الإشارة إليه) ويمكن تغيير القيم الموجودة في الصفيف خارج الفصل بعد ذلك. سأقوم بنسخة ضحلة من المصفوفة ، ولكن أيضًا قم بتوثيق المشكلة المحتملة مع tuples ذات القيم غير القابلة للتطبيق (يمكن تغييرها خارج Tuple).لك
equals
الطريقة تفتقر إلى الفحص الفارغ (if (obj == null) return false
) وفحص الفصل (إماobj instanceof Tuple
أوthis.getClass().equals(object.getClass())
). تم توثيق المصطلح المتساوي جيدًا.لا توجد طريقة لعرض قيم tuple إلا من خلال
toString
. هذا يحمي القيم وعدم قابلية الثبات الكلي ، لكنني أعتقد أنه يحد من فائدة الفصل.على الرغم من أنني أدرك أنه مجرد مثال ، إلا أنني لا أتوقع استخدام هذا الفصل لشيء مثل أعياد الميلاد/التواريخ. في مجالات الحل مع أنواع الكائنات الثابتة ، تكون الفئات الحقيقية (مثل التاريخ) أفضل بكثير. أتصور أن هذا الفئة مفيدة في مجالات محددة حيث تكون tuples كائنات من الدرجة الأولى.
يحرركنت أفكر في هذا. ها هي رأيي في بعض الكود (على جيثب + الاختبارات):
===
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)
نصائح أخرى
كيف يكون هذا الأنواع ملهمة؟ أنت ترمي استثناءات وقت التشغيل بدلاً من الإبلاغ عن الأخطاء في وقت الترجمة.
أنت تحاول التجريد على Arity وهو (حتى الآن) غير ممكن في اللغات المكتوبة بشكل ثابت ، دون فقدان السلامة.
إضافة:
يمكن أن تتكون tuples من عناصر غير متجانسة (أي عناصر ذات أنواع مختلفة). لذلك فإن توفير حتى "أنواع الأنواع المتقدمة" غير ممكن ، لذلك Tuple
صف دراسي. عملاء الفصل مسؤولون عن صنع الممثلين المناسبة.
هذا هو أفضل ما يمكنك القيام به في جافا: (يحرر: نرى منشور برنت لتنفيذ أفضل ل Tuple
. (لا يتطلب typecasts على جانب العميل.))
final class Tuple {
private final List<Object> elements;
public Tuple(final Object ... elements) {
this.elements = Arrays.asList(elements);
}
@Override
public String toString() {
return elements.toString();
}
//
// Override 'equals' and 'hashcode' here
//
public Object at(final int index) {
return elements.get(index);
}
}
هذا هو أبسط الحلول وهو الأفضل أيضًا. إنه مشابه لكيفية تمثيل tuples في .NET. إنه يتجنب بعناية محو جافا. يتم كتابتها بقوة. انها لا ترمي الاستثناءات. إنه سهل الاستخدام للغاية.
public interface Tuple
{
int size();
}
public class Tuple2<T1,T2> implements Tuple
{
public final T1 item1;
public final T2 item2;
public Tuple2(
final T1 item_1,
final T2 item_2)
{
item1 = item_1;
item2 = item_2;
}
@Override
public int size()
{
return 2;
}
}
public class Tuple3<T1,T2,T3> implements Tuple
{
public final T1 item1;
public final T2 item2;
public final T3 item3;
public Tuple3(
final T1 item_1,
final T2 item_2,
final T3 item_3)
{
item1 = item_1;
item2 = item_2;
item3 = item_3;
}
@Override
public int size()
{
return 3;
}
}
يجب أن تنظر إلى تنفيذ .NET's Tuple. فهي آمنة للوقت.
ما هو الغرض من typeLock
؟ للسماح لشخص ما بمنع بناء المزيد من هذه الأشياء؟ هذا الجزء لا معنى له.
لماذا تريد أن تدع شخصًا ما يمنع المزيد من تثبيت الأشياء الخاصة بك؟ إذا كان هذا شيء تحتاجه لسبب ما ، بدلاً من "قفل" فئة ورمي استثناءات ، فقط تأكد من أن مسار الكود ... لا ينشئ المزيد من الكائنات من النوع.
ما هو الغرض من الثابت lastTuple
الذي تم تعيينه على مرجع آخر مثيل له Tuple
؟ إنها ممارسة سيئة لخلط المراجع الثابتة مثل هذا.
بصراحة ، فإن الكود مربك للغاية ، على الرغم من أن الحاجة إلى هذه الفئة مربكة. إذا كان هذا بطريقة أو بأخرى رمزًا كنت أراجعه في بيئة عمل ، فلن أسمح بذلك.
شاهد هذا الرمز في مشروع الموجة
public class Tuple<A> {
private final A[] elements;
public static <A> Tuple<A> of(A ... elements) {
return new Tuple<A>(elements);
}
public Tuple(A ... elements) {
this.elements = elements;
}
public A get(int index) {
return elements[index];
}
public int size() {
return elements.length;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || o.getClass() != this.getClass()) {
return false;
}
Tuple<A> o2 = (Tuple<A>) o;
return Arrays.equals(elements, o2.elements);
}
@Override
public int hashCode() {
return Arrays.hashCode(elements);
}
@Override
public String toString() {
return Arrays.toString(elements);
}
}
إليك تطبيق N-Tuple فظيع حقًا يستخدم الأداء العام لتوفير فحوصات نوع الترجمة. تُظهر الطريقة الرئيسية (المنصوص عليها لأغراض تجريبية) مدى رعب ذلك في الاستخدام:
interface ITuple { }
/**
* Typed immutable arbitrary-length tuples implemented as a linked list.
*
* @param <A> Type of the first element of the tuple
* @param <D> Type of the rest of the tuple
*/
public class Tuple<A, D extends ITuple> implements ITuple {
/** Final element of a tuple, or the single no-element tuple. */
public static final TupleVoid END = new TupleVoid();
/** First element of tuple. */
public final A car;
/** Remainder of tuple. */
public final D cdr;
public Tuple(A car, D cdr) {
this.car = car;
this.cdr = cdr;
}
private static class TupleVoid implements ITuple { private TupleVoid() {} }
// Demo time!
public static void main(String[] args) {
Tuple<String, Tuple<Integer, Tuple<String, TupleVoid>>> triple =
new Tuple<String, Tuple<Integer, Tuple<String, TupleVoid>>>("one",
new Tuple<Integer, Tuple<String, TupleVoid>>(2,
new Tuple<String, TupleVoid>("three",
END)));
System.out.println(triple.car + "/" + triple.cdr.car + "/" + triple.cdr.cdr.car);
//: one/2/three
}
}
إذا كنت مهتمًا حقًا بكتابة حاويات آمنة من النوع ، فابحث في الأدوية:
public class Tuple<T> {
private final T[] arr;
public Tuple (T... contents) {
arr = contents; //not sure if this compiles??
}
// etc
public static final void main(String[] args) {
Tuple<String> stringTuple = new Tuple<String>("Hello", "World!");
Tuple<Integer> intTuple = new Tuple<Integer>(2010,9,4);
}
}
سيكون من الأفضل استخدام الأدوية الجيرية لتجميع سلامة نوع الوقت. يمكنك تحديد واجهة واحدة لكل القوس. ثم يمكنك تحديد واجهات منفصلة قابلة للاتصال للوصول إلى قيم tuple.
interface Tuple1 <T0> { <R> R accept ( Callable1<R,T0> callable ) ; }
interface Tuple2 <T0,T1> { <R> R accept ( Callable2<R,T0,T1> callable ) ; }
...
interface Tuplek <T0,T1,T2,...,Tk> { <R> R accept ( Callablek<R,T0,T1,T2,...,Tk> callable ) ; }
interface Callable1<R,T0> { R call ( T0 t0 ) ; }
interface Callable2<R,T0> { R call ( T0 t0 , T1 t1 ) ; }
....
interface Callablek<R,T0,T1,T2,...,Tk> { R call ( T0 t0 , T1 t1 , T2 t2 , ... , Tk tk ) ; }