Java多类型方法参数?

ege*_*lev 17 java design-patterns code-duplication

我想知道是否有可能要求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
   }
Run Code Online (Sandbox Code Playgroud)

我想避免代码重复.我的想法是这样的:

   public void useMyMethod(A|B obj){
      obj.myMethod();
   }
Run Code Online (Sandbox Code Playgroud)

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

  try{
     //fail
  } catch (IllegalArgumentException | IllegalStateException e){
     // use e safely here
  }
Run Code Online (Sandbox Code Playgroud)

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

Yog*_*ter 15

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

如果您使用的是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;
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

对于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());
}
Run Code Online (Sandbox Code Playgroud)

  • 不错但它强迫我们向调用者公开我们将在参数上调用的方法.更改实现(简单地调用另一个方法)可能会强制我们去编辑函数的每次使用.如果我们想要调用多种方法,它很快就会变得单调乏味. (2认同)
  • 嘿@ 5gon12ender,你的积分是有效的.使用适配器具有封装对方法的调用的好处,因此如果需要更改方法,则只需更改适配器类.另一方面,您需要为每个接口创建一个适配器,这比功能解决方案更加繁琐.它实际上取决于代码的范围(是小类的内部代码还是广泛使用的函数?)以及改变实现的可能性,我想说. (2认同)

pbe*_*nyi 13

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

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

public interface Base {
    void myMethod();
}

public interface A extends Base {}
public interface B extends Base {}
...
public void useMyMethod(Base b) {
    b.myMethod()
}
Run Code Online (Sandbox Code Playgroud)

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

  • 您可能错过了"如何使用此类不可编辑的类型层次结构实现设计良好的代码?"部分. (4认同)
  • 由于Base没有所需的方法,因此无法正常工作. (3认同)

Pau*_*ton 8

你可以interface MyInterface用一种方法写一个myMethod.然后,对于要考虑作为有限集的一部分的每种类型,编写一个包装类,如下所示:

class Wrapper1 implements MyInterface {

    private final Type1 type1;

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

    @Override
    public void myMethod() {
        type1.method1();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你只需要使用一个MyInterface而不是一个有限类型的类型,并且总是会调用相应类型的适当方法.

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

myMethod(new Wrapper1(type1));
Run Code Online (Sandbox Code Playgroud)

这将变得有点难看,因为您将不得不记住集合中每种类型的包装类的名称.因此,您可能更愿意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();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用代码调用方法

myMethod(MyWrapper.of(type1));
Run Code Online (Sandbox Code Playgroud)

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


Bar*_*W19 5

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

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

public static void useMyMethod(Object classAorB) throws Exception {
    classAorB.getClass().getMethod("myMethod").invoke(classAorB);
}
Run Code Online (Sandbox Code Playgroud)

  • 为了至少在调用者的站点上使这种静态类型安全,我考虑使用接受`Object``private`的方法,并为每个要支持的类型提供重载的'public`方法.这些只是委托你显示的方法.这确实需要一些代码重复,但只需要技术单行,不需要任何业务逻辑. (3认同)