Object以外の親型を共有しない2つの型のいずれかのオブジェクトをパラメーターとして受け入れるメソッドを作成しています。たとえば、タイプはDreamsとGarlicです。 dreams.crush()
とgarlic.crush()
の両方を実行できます。 DreamsとGarlicの両方をパラメーターとして受け入れるutterlyDestroy(parameter)
メソッドが必要です。
_utterlyDestroy(parameter) {
parameter.crush()
}
_
ニンニクと夢はどちらも一部のライブラリの一部であるため、ICrushableインターフェイスを実装する(utterlyDestroy(ICrushable parameter)
を作成できるようにする)オプションはありません。
私のメソッド本体は非常に長いので、それをオーバーロードすると、コードが重複することになります。醜い。リフレクションを使用して、クラスハッキングを実行できると確信しています。醜い。
ジェネリックを使用してみましたが、
_utterlyDestroy(<T instanceof Dreams || T instanceof Garlic> parameter)
_
ニンニクを夢に型キャストすることは可能ですか?
_utterlyDestroy(Object parameter) {
((Dreams)parameter).crush()
}
_
それでもこれはいでしょう。私の他のオプションは何ですか?また、状況に対処する好ましい方法は何ですか?
これはどう:
interface ICrushable {
void crush();
}
utterlyDestroy(ICrushable parameter) {
// Very long crushing process goes here
parameter.crush()
}
utterlyDestroy(Dreams parameter) {
utterlyDestroy(new ICrushable() { crush() {parameter.crush();});
}
utterlyDestroy(Garlic parameter) {
utterlyDestroy(new ICrushable() { crush() {parameter.crush();});
}
新しい開発ではICrushableインターフェイスを実装する必要がありますが、既存のクラスの場合、パラメーターはICrushableにラップされ、すべての作業を行うutterlyDestroy(ICrushable)に渡されます。
これほど簡単なものはどうですか?
utterlyDestroy(Object parameter) {
if(parameter instanceOf Dreams){
Dream dream = (Dreams)parameter;
dream.crush();
//Here you can use a Dream
}
else if(parameter instanceOf Garlic){
Garlic garlic = (Garlic)parameter;
//Here you can use a Garlic
garlic.crush();
}
}
utterlyDestroy
が複雑すぎて大きく、単にcrush
を呼び出したい場合、これはあなたが望むことをします
Haskell風のいずれかのクラスをJavaで実装できます。このようなもの:
class Either<L,R>
{
private Object value;
public static enum Side {LEFT, RIGHT}
public Either(L left) {value = left;}
public Either(R right) {value = right;}
public Side getSide() {return value instanceof L ? Side.LEFT : Side.RIGHT;}
// Both return null if the correct side isn't contained.
public L getLeft() {return value instanceof L ? (L) value : null;}
public R getRight() {return value instanceof R ? (R) value : null;}
}
次に、そのメソッドにタイプEither<Dreams, Garlic>
。
インターフェイスと adapt を使用できます。
インターフェース:
public interface Crushable {
public void crush();
}
呼び出しの例:
public class Crusher {
public static void crush(Crushable crushable) {
crushable.crush();
}
}
アダプターファクトリメソッドの例:
public final class Dreams {
public static Crushable asCrushable(final Dream dream) {
class DreamCrusher implements Crushable {
@Override
public void crush() {
dream.crush();
}
}
return new DreamCrusher();
}
private Dreams() {}
}
消費者コードは次のようになります。
Dream dream = new Dream();
Crushable crushable = Dreams.asCrushable(dream);
Crusher.crush(crushable);
適応するタイプが多数ある場合は、リフレクションを検討できます。 Proxy タイプを使用する(最適化されていない)アダプターファクトリを次に示します。
public final class Crushables {
private static final Class<?>[] INTERFACES = { Crushable.class };
public static Crushable adapt(final Object crushable) {
class Handler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return crushable.getClass()
.getMethod(method.getName(), method.getParameterTypes())
.invoke(crushable, args);
}
}
ClassLoader loader = Thread.currentThread()
.getContextClassLoader();
return (Crushable) Proxy.newProxyInstance(loader, INTERFACES, new Handler());
}
private Crushables() {}
}
APIの消費者にとって、これはそれほど見苦しくありません。
Dream dream = new Dream();
Crushable crushable = Crushables.adapt(dream);
Crusher.crush(crushable);
ただし、通常のリフレクションでは、コンパイル時の型チェックを犠牲にします。
メソッドのオーバーロードを使用するだけです。
public void utterlyDestroy(Dreams parameter) {
parameter.crush();
}
public void utterlyDestroy(Garlic parameter) {
parameter.crush();
}
これら2つのタイプ以外を同じ方法でサポートする場合は、それらすべてに共通のインターフェースを定義し、ジェネリックを使用できます。
Interface Crushableの作成は、最もクリーンな方法のようです。ニンニクまたはドリームのサブタイプはオプションですか?サブタイプにインターフェースを追加しますか?
それがなければ、共通のコードをプライベートメソッドに入れて、2つのバージョンのutterlyDestroyに、共通のコードを呼び出す前に個々のオブジェクトに対して実行する処理を実行させることができます。メソッド本体が長い場合は、とにかくそれをプライベートメソッドに分割する必要があります。ただし、インターフェイスを追加するよりもさらに明白な解決策であるため、すでにこれを考えていたと思います。
パラメーターをオブジェクトとして取り込み、キャストすることができます。これはあなたが反射によって意味するものですか?すなわち、
public void utterlyCrush(Object crushable) {
if (crushable instanceOf Dream) {
...
}
if (curshable instanceOf Garlic) {
...
}
ただし、ニンニクからドリームへのキャストは、一方が他方のサブタイプではない場合、オプションではありません。
私が使用しているように:
void fooFunction(Object o){
Type1 foo=null;
if(o instanceof Type1) foo=(Type1)o;
if(o instanceof Type2) foo=((Type2)o).toType1();
// code
}
ただし、Type2をType1に変換できる場合にのみ機能します