繁体   English   中英

Java Generics - 接受不同通用接口的方法

[英]Java Generics - method which accept different generic interfaces

说我有这个代码:

IOperation<?> parameter1 = null;
ITotallyDifferentOperation<?> parameter2 = null;

switch (OPERATION_TYPE) {
  case TYPE_1:
    parameter1 = new MyOperation<Type1>();
    parameter2 = new MyTotallyDifferentOperation<Type1>();
    break;
  case TYPE_2:
    parameter1 = new MyOperation<Type2>();
    parameter2 = new MyTotallyDifferentOperation<Type2>();
    break;

...

switch (DB_OPERATION_TYPE) {
  case DB_TYPE_1:
    myMethod(parameter1, parameter2);
    break;
  case DB_TYPE_2:
    myOtherMethod(parameter1, parameter2);
    break;

...

接受MyOperationMyTotallyDifferentOperation实现的两个interfaces的方法:

void myMethod(final IOperation<?> operation, final ITotallyDifferentOperation<?> totallyDifferentOperation) {
  operation.processList(totallyDifferentOperation.getList());
}

totallyDifferentOperation.getList()返回一个List<T> ,它与operation.processList()接受的List<T>类型相同。

这段代码显然无法编译。
是否有其他模式可以获得相同的结果,或者可以更正此代码?

根据要求,我发布了更多的方法。 不幸的是,我不能再透露,这是一个模型。
我需要这种模式以避免重复代码。

没有既定的设计模式可以帮助您。


您的问题在于您声明这些变量:

IOperation<?> parameter1 = null;
ITotallyDifferentOperation<?> parameter2 = null;

通过使用通配符,您可以有效地告诉编译器“我不关心类型”。 这不是真的。 最终你会关心这种类型。

从根本上说,你的方法试图做太多,这就是造成你问题的原因。 将其分解为多种方法,它变得更加容易:

switch (OPERATION_TYPE) {
   case TYPE_1:
      typeOne();
   case TYPE_2:
      typeTwo();
}

private void typeOne()
{
    IOperation<Type1> parameter1 = new MyOperation<>();
    ITotallyDifferentOperation<Type1> parameter2 = new MyTotallyDifferentOperation<>();

    doTheChecks(parameter1, parameter2);
    databaseStuff(parameter1, parameter2);
}

private void typeTwo() /*identical to above, except the types*/
{
    IOperation<Type2> parameter1 = new MyOperation<>();
    ITotallyDifferentOperation<Type2> parameter2 = new MyTotallyDifferentOperation<>();

    doTheChecks(parameter1, parameter2);
    databaseStuff(parameter1, parameter2);
}

<T> void doTheChecks(IOperation<T> param1, ITotallyDifferentOperation<T> param2)
{
    ...
}

<T> void databaseStuff(IOperation<T> param1, ITotallyDifferentOperation<T> param2)
{
    switch (DB_OPERATION_TYPE) {
        case DB_TYPE_1:
            myMethod(param1, param2);
            break;
        case DB_TYPE_2:
            myOtherMethod(param1, param2);
            break;
    }
}

阅读关于泛型方法参数的Java教程 您可以向方法添加类型参数,以确保操作和fullyDifferentOperation适用于相同的类型:

<T> void myMethod(IOperation<T> operation,
                  ITotallyDifferentOperation<T> totallyDifferentOperation)
{
    operation.processList(totallyDifferentOperation.getList());
}

这样您就可以确保两个参数的输入方式与调用者相同:

// compiles
myMethod(new MyOperation<Type1>(), new MyTotallyDifferentOperation<Type1>())
// does not compile
myMethod(new MyOperation<Type1>(), new MyTotallyDifferentOperation<Type2>())

这是最简单的例子。 您可以使用上限和下限来使限制更灵活。 这可能使您能够混合类型,只要它们是兼容的(想象一个可以处理任何Number输入的IOperation,以及提供LongDouble ,......的不同ITotallyDifferentOperations)。 本教程解释了这一点。


添加代码后,也许你可以看看这个:

Operations<?> parameters = null;

switch (OPERATION_TYPE)
{
    case TYPE_1 :
        parameters = new Operations<>(new MyOperation<Type1>(), 
                                      new MyTotallyDifferentOperation<Type1>());
        break;
}
switch (DB_OPERATION_TYPE)
{
    case DB_TYPE_1 :
        myMethod(parameters);
        break;
}

static <T> void myMethod(final Operations<T> operations)
{
    operations.operation.processList(operations.totallyDifferentOperation.getList());
}

static class Operations<T>
{
    public final IOperation<T>                 operation;
    public final ITotallyDifferentOperation<T> totallyDifferentOperation;

    public Operations(IOperation<T> operation, ITotallyDifferentOperation<T> totallyDifferentOperation)
    {
        this.operation = operation;
        this.totallyDifferentOperation = totallyDifferentOperation;
    }
}

通过将两个操作包装到类型化参数对象中,可以确保它们属于同一类型。 这当然意味着你的dbOperations需要单独接受param对象而不是两个参数。

我假设你的Operation类和接口看起来像这样:

interface IOperation<T> {
    public List<T> processList(List<T> list);
    public List<T> getList();
}

interface ITotallyDifferentOperation<T> {
    public List<T> processList(List<T> list);
    public List<T> getList();
}

class MyOperation<T> implements IOperation<T> {
    public List<T> processList(List<T> list) {return null;}
    public List<T> getList() {return null;}
}

class MyTotallyDifferentOperation<T> implements ITotallyDifferentOperation<T> {
    public List<T> processList(List<T> list) {return null;}
    public List<T> getList() {return null;}
}

而你得到的错误是这样的:

void myMethod(final IOperation<?> parameter1,
final ITotallyDifferentOperation<?> parameter2) {
    // COMPILE ERROR: processList() is not applicable for the arguments
    parameter1.processList(parameter2.getList());
}

编译错误的原因是myMethod()允许参数1和参数2的类型彼此不同。

最好的选择(在我看来) 不是通配符参数1和参数2,而是确保它们是相同的泛型类型,如下所示:

Operation<T> parameter1 = null;
ITotallyDifferentOperation<T> parameter2 = null;

基于IOperation接口,这些操作中的任何一个都需要myMethod()接受具有相同泛型类型的两个参数,如下所示:

void <T> myMethod(
final IOperation<T> operation,
final ITotallyDifferentOperation<T> totallyDifferentOperation) {
    operation.processList(totallyDifferentOperation.getList());
}

如果您不想执行上述任何操作,则可以更改两个IOperation接口processList方法以接受通配符,以便您可以将所有输入保留为通配符,如下所示:

interface IOperation<T> {
    public List<T> processList(List<?> list);
    public List<T> getList();
}

interface ITotallyDifferentOperation<T> {
    public List<T> processList(List<?> list);
    public List<T> getList();
}

那么唯一的问题是你在这些方法中没有任何编译时类型保证,你必须在运行时检查它们......

编辑回应Malte Hartwig:

不通配符参数将无法编译,因为T无法解析。 这就是整个问题:你不知道它是哪种类型

你不需要知道它是什么类型,只是它们是相同的类型T 我在这里扩展了我的答案:

// remove wildcards, use a generic method, rather than deciding Type in a switch statment.
<T> void someMethod(final Class<T> klass, final int DB_OPERATION_TYPE) {
    IOperation<T> parameter1 = new MyOperation<T>();
    ITotallyDifferentOperation<T> parameter2 = new MyTotallyDifferentOperation<T>();

// ... OR ...

<T> void someMethod(final int DB_OPERATION_TYPE, IOperation<T> parameter1,
    ITotallyDifferentOperation<T> parameter2) {

    // ... Other conditions to evaluate before calling the myMethod ...

    switch(DB_OPERATION_TYPE) {
        case 1:
            myMethod(parameter1, parameter2);
            break;
        case 2:
            myOtherMethod(parameter1, parameter2);
            break;
    }
}

<T> void myMethod(final IOperation<T> operation, final ITotallyDifferentOperation<T> totallyDifferentOperation) {
    operation.processList(totallyDifferentOperation.getList());
}

<T> void myOtherMethod(final IOperation<T> operation, final ITotallyDifferentOperation<T> totallyDifferentOperation) {
    // something... ?
} 

并调用此方法:

final int DB_OPERATION_TYPE = 1;
someMethod(String.class, DB_OPERATION_TYPE);
// OR
someMethod(DB_OPERATION_TYPE, new MyOperation<String>(), MyTotallyDifferentOperation<String>());

至于将方法调用带入交换机并完全抛弃参数:Op澄清说这不是一个选项。

只有在我的回答发布后,Op才会回复您。 我现在已经根据我的回答编辑了它。

阐述迈克尔回答这是我提出的问题。 好像差不多 ;)

switch (operation) {
  case TYPE_1:
    processDbOperation(
      dbOperation, 
      new MyOperation<Type1>(), 
      new MyTotallyDifferentOperation<Type1>());
    break;
  case TYPE_2:
    processDbOperation(
      dbOperation, 
      new MyOperation<Type2>(),
      new MyTotallyDifferentOperation<Type2>());
    break;

...

<T> void processDbOperation(final DbOperation dbOperation, final IOperation<T> operation, final ITotallyDifferentOperation<T> totallyDifferentOperation) {
  switch (dbOperation) {
    case DB_TYPE_1:
      myMethod(parameter1, parameter2);
      break;
    case DB_TYPE_2:
      myOtherMethod(parameter1, parameter2);
      break;
}

...

另外传递描述类型参数的Class对象怎么样? 然后基本上你有非擦除类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM