java Java多类型方法参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27503251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 11:54:58  来源:igfitidea点击:

Java multi-type method parameter?

javadesign-patternscode-duplication

提问by egelev

I wonder if it is possible to require that a java method parameter is of any type from finite set of types. For example - I am using a library where two (or more) types have common methods, but their lowest common ancestor in the type hierarchy is Object. What I mean here:

我想知道是否有可能要求 java 方法参数是有限类型集中的任何类型。例如 - 我正在使用一个库,其中两个(或更多)类型具有公共方法,但它们在类型层次结构中最低的公共祖先是 Object。我在这里的意思是:

   public interface A {
      void myMethod();
   }

   public interface B {
      void myMethod();
   }
...
   public void useMyMethod(A a) {
      // code duplication
   }

   public void useMyMethod(B b) {
      // code duplication
   }

I want to avoid the code duplication. What I think of is something like this:

我想避免代码重复。我想到的是这样的:

   public void useMyMethod(A|B obj){
      obj.myMethod();
   }

There is similar type of syntax in java already. For example:

java中已经有类似类型的语法。例如:

  try{
     //fail
  } catch (IllegalArgumentException | IllegalStateException e){
     // use e safely here
  }

Obviously this is not possible. How can I achieve well designed code using such type of uneditable type hierarchy ?

显然这是不可能的。如何使用这种类型的不可编辑的类型层次结构来实现设计良好的代码?

采纳答案by Paul Boddington

You could write an interfaceMyInterfacewith a single method myMethod. Then, for each type you want to consider as part of the finite set, write a wrapper class, like this:

您可以interfaceMyInterface使用单个方法编写一个myMethod。然后,对于您要考虑为有限集一部分的每种类型,编写一个包装类,如下所示:

class Wrapper1 implements MyInterface {

    private final Type1 type1;

    Wrapper1(Type1 type1) {
        this.type1 = type1;
    }

    @Override
    public void myMethod() {
        type1.method1();
    }
}

Then you just need to use a MyInterfacerather than one of the finite set of types, and the appropriate method from the appropriate type will always get called.

然后你只需要使用一个MyInterface而不是有限类型集的一个,并且来自适当类型的适当方法将始终被调用。

Note that to actually use these wrapper classes to call the method myMethodyou would have to write

请注意,要实际使用这些包装类来调用myMethod您必须编写的方法

myMethod(new Wrapper1(type1));

This is going to get a bit ugly as you are going to have to remember the name of the wrapper class for each type in the set. For this reason, you may prefer to replace MyInterfacewith an abstract class with several static factories that produce the wrapper types. Like this:

这将变得有点难看,因为您将不得不记住集合中每种类型的包装类的名称。出于这个原因,您可能更喜欢MyInterface用一个抽象类替换几个产生包装器类型的静态工厂。像这样:

abstract class MyWrapper {

    static MyWrapper of(Type1 type1) {
        return new Wrapper1(type1);
    }

    static MyWrapper of(Type2 type2) {
        return new Wrapper2(type2);
    }

    abstract void myMethod();
}

then you can call the method using the code

然后您可以使用代码调用该方法

myMethod(MyWrapper.of(type1));

The advantage of this approach is that the code is the same no matter which type you use. If you use this approach you have to replace implements MyInterfacein the Wrapper1declaration with extends MyWrapper.

这种方法的优点是无论您使用哪种类型,代码都是相同的。如果你使用这种方法,你必须implements MyInterfaceWrapper1声明中用extends MyWrapper.

回答by Yogster

What about passing the function as a parameter to your useMyMethod function?

将该函数作为参数传递给 useMyMethod 函数怎么样?

If you are using Java < 8:

如果您使用的是 Java < 8:

public interface A {
    void myMethod();
}

public interface B {
    void myMethod();
}

public void useMyMethod(Callable<Void> myMethod) {
    try {
        myMethod.call();
    } catch(Exception e) {
        // handle exception of callable interface
    }
}

//Use

public void test() {
    interfaceA a = new ClassImplementingA();
    useMyMethod(new Callable<Void>() {
        public call() {
            a.myMethod();
            return null;
        }
    });

    interfaceB b = new ClassImplementingB();
    useMyMethod(new Callable<Void>() {
        public call() {
            b.myMethod();
            return null;
        }
    });
}

For Java >= 8, you could use Lambda Expressions:

对于 Java >= 8,您可以使用Lambda 表达式

public interface IMyMethod {
    void myMethod();
}

public void useMyMethod(IMyMethod theMethod) {
    theMethod.myMethod();
}

//Use

public void test() {
    interfaceA a = new ClassImplementingA();
    useMyMethod(() -> a.myMethod());

    interfaceB b = new ClassImplementingB();
    useMyMethod(() -> b.myMethod());
}

回答by pbespechnyi

Try using Adapterdesign pattern.

尝试使用适配器设计模式。

Or, if it's possible, add some base interface:

或者,如果可能,添加一些基本接口:

public interface Base {
    void myMethod();
}

public interface A extends Base {}
public interface B extends Base {}
...
public void useMyMethod(Base b) {
    b.myMethod()
}

Also, you can use something similar to this

此外,您还可以使用类似的东西这个

回答by BarrySW19

Well, the correct way to model your requirement would be to have myMethod() declared in a supertype interface C which both A and B extend; your method then accepts type C as its parameter. The fact that you have trouble doing this in the situation you describe indicates you are not modelling the class hierarchy in a way that actually reflects how they behave.

好吧,为您的需求建模的正确方法是在 A 和 B 都扩展的超类型接口 C 中声明 myMethod();然后您的方法接受类型 C 作为其参数。在您所描述的情况下执行此操作时遇到问题这一事实表明您没有以实际反映它们行为方式的方式对类层次结构进行建模。

Of course, if you can't change the interface structure then you could always do it with reflections.

当然,如果你不能改变界面结构,那么你总是可以用反射来做。

public static void useMyMethod(Object classAorB) throws Exception {
    classAorB.getClass().getMethod("myMethod").invoke(classAorB);
}

回答by OldCurmudgeon

This looks to me much like the template pattern:

这在我看来很像模板模式:

public interface A {

    void myMethod();
}

public interface B {

    void myMethod();
}

public class C {

    private abstract class AorBCaller {

        abstract void myMethod();

    }

    public void useMyMethod(A a) {
        commonAndUseMyMethod(new AorBCaller() {

            @Override
            void myMethod() {
                a.myMethod();
            }
        });
    }

    public void useMyMethod(B b) {
        commonAndUseMyMethod(new AorBCaller() {

            @Override
            void myMethod() {
                b.myMethod();
            }
        });
    }

    private void commonAndUseMyMethod(AorBCaller aOrB) {
        // ... Loads of stuff.
        aOrB.myMethod();
        // ... Loads more stuff
    }
}

In Java 8 it is much more succinct:

在 Java 8 中,它更加简洁:

public class C {

    // Expose an "A" form of the method.
    public void useMyMethod(A a) {
        commonAndUseMyMethod(() -> a.myMethod());
    }

    // And a "B" form.
    public void useMyMethod(B b) {
        commonAndUseMyMethod(() -> b.myMethod());
    }

    private void commonAndUseMyMethod(Runnable aOrB) {
        // ... Loads of stuff -- no longer duplicated.
        aOrB.run();
        // ... Loads more stuff
    }
}

回答by zenzizenzizenzic

This might not constitute a best practice, but could you make a new class (call it C), that contains the parts from A and B that are duplicated, and the make a new method that takes C, have your methods that take A and B make a C instance and call the new method?

这可能不是最佳实践,但是您能否创建一个新类(称为 C),其中包含 A 和 B 中重复的部分,并创建一个采用 C 的新方法,让您的方法采用 A 和B 创建一个 C 实例并调用新方法?

So that you have

所以你有

class C {
    // Stuff from both A and B
}

public void useMyMethod(A a) {
    // Make a C
    useMyMethod(c);
}

public void useMyMethod(B b) {
    // Make a C
    useMyMethod(c);
}

public void useMyMethod(C c) {
    // previously duplicated code
}

That would also let you keep any non duplicated code in the methods for A and B (if there is any).

这也可以让您在 A 和 B 的方法中保留任何非重复的代码(如果有的话)。

回答by Jord?o

A dynamic proxy can be used to create a bridge between a common interface you define and the objects implementing the other interfaces that conformto the new interface. Then, you can have your useMyMethods convert the parameter to the new interface (as a dynamic proxy) and have your common code written in terms only of the new interface.

动态代理可用于在您定义的公共接口与实现符合新接口的其他接口的对象之间建立桥梁。然后,您可以让您的useMyMethods 将参数转换为新接口(作为动态代理),并仅根据新接口编写公共代码。

This would be the new interface:

这将是新界面:

interface Common {
  void myMethod();
}

Then, with this invocation handler:

然后,使用此调用处理程序:

class ForwardInvocationHandler implements InvocationHandler {
  private final Object wrapped;
  public ForwardInvocationHandler(Object wrapped) {
    this.wrapped = wrapped;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    Method match = wrapped.getClass().getMethod(method.getName(), method.getParameterTypes());
    return match.invoke(wrapped, args);
  }
}

You can have your methods like this:

你可以有这样的方法:

public void useMyMethod(A a) {
  useMyMethod(toCommon(a));
}

public void useMyMethod(B b) {
  useMyMethod(toCommon(b));
}

public void useMyMethod(Common common) {
  // ...
}

private Common toCommon(Object o) {
  return (Common)Proxy.newProxyInstance(
    Common.class.getClassLoader(), 
    new Class[] { Common.class }, 
    new ForwardInvocationHandler(o));   
}

Note that to simplify matters you could even elect one of your existing interfaces (Aor B) to be used as the common interface.

请注意,为了简化问题,您甚至可以选择现有接口之一(AB)用作公共接口。

(Look at another example here, and also at other ideas around this subject)

(看看这里的另一个例子,以及围绕这个主题的其他想法)

回答by StarShine

The correct way is to use Java Generics.

正确的方法是使用Java泛型。

See http://docs.oracle.com/javase/tutorial/java/generics/bounded.html

请参阅http://docs.oracle.com/javase/tutorial/java/generics/bounded.html