【问题标题】:Get type of elements in List获取 List 中元素的类型
【发布时间】:2018-05-15 20:41:56
【问题描述】:

我有两个不同的列表:

List<MyFirstClass> list1;
List<MySecondClass> list2;

现在我需要一个将List 作为输入的方法。对于两个列表,我在这种方法中所做的事情几乎相同,只是略有不同。因为我不能只是超载它们,所以我这样做了:

private String myMethod(List<?> myList) {...}

但现在我需要检查? 是什么类型,以便在方法上与给出的列表元素类型有细微的差别。

我该怎么做?或者有没有更好的方法来做到这一点?

谢谢!

编辑 除了 one 方法调用list1.method1()list2.method2() 之外,这两个方法实际上是相同的。这就是为什么我不想制作两种不同的方法。

【问题讨论】:

  • instanceof 是你的领导
  • 在泛型中检查? 的需要很好地说明了该方法首先不是泛型的理想候选者。最好使用两种方法,它们可能通过调用公共私有方法来共享公共逻辑。
  • 如果没有更多代码,很难做出有意义的回答。
  • @Yahya 不,instanceof 是你的敌人。它导致脆弱的 OOP、难以扩展和容易出错的程序。请不要鼓励学习开发者依赖instanceof
  • @progNewbie 虽然可能有多种解决方法,但如果没有更多代码,您发布XY problem 的风险很大,在这种情况下,您收到的建议可能会解决问题,但是从长远来看,这不会真正使您受益。

标签: java list generics


【解决方案1】:

字面的解决方法是使用instanceof来区分对象的类型。

private void myMethod(List<?> list) {
    for(Object o : list) {
        if(o instanceof MyFirstClass) {
            MyfirstClass p = (MyFirstClass)o;
            p.myFirstMethod();
        } else if(o instanceof MySecondClass) {
            MySecondClass p = (MyFirstClass)o;
            p.mySecondMethod();
        }
    }
}

如果您项目的设计约束是不可变的,那么这是您最好的解决方案。

但更好的解决方案使用继承

这个问题确实表明您几乎可以肯定您的整体设计存在缺陷。如果您不/不能提前知道您是否会得到List&lt;MyFirstClass&gt;List&lt;MySecondClass&gt;,那么这是非常有力的证据,您的问题域定义不明确。最好通过让它们实现一个接口来执行必要的约束,该接口指示在这种情况下行为应该如何不同。

public interface MyInterface {
    void invokeMyMethod();
}

public class MyFirstClass implements MyInterface {
    public void invokeMyMethod() {myFirstMethod();}
    /*...*/
}
public class MySecondClass implements MyInterface {
    public void invokeMyMethod() {mySecondMethod();}
    /*...*/
}

//In some other class...

private void myMethod(List<MyInterface> list) {
    for(MyInterface o : list) {
        o.invokeMyMethod();
    }
}

myMethod 也可以在您不确定是否会得到List&lt;MyFirstClass&gt;List&lt;MySecondClass&gt;List&lt;MyInterface&gt; 的情况下重写:

private <T extends MyInterface> void myMethod(List<T> list) {
    for(MyInterface o : list) {
        o.invokeMyMethod();
    }
}

【讨论】:

    【解决方案2】:

    您不能根据List&lt;?&gt; 中的类型直接调整您的方法。不过,还有其他方法可以得到你想要的。这是一种方法(这些名字会很糟糕,因为我没有太多关于你想要做的事情的上下文):

    interface MethodCaller<T> {
      void callMethod(T target);
    }
    
    class Class1Caller implements MethodCaller<MyFirstClass> {
      @Override
      public void callMethod(MyFirstClass target) {
        target.method1();
      }
    }
    
    class Class1Caller implements MethodCaller<MySecondClass> {
      @Override
      public void callMethod(MySecondClass target) {
        target.method2();
      }
    }
    

    然后更改您的方法以获取与列表相同类型的MethodCaller,如下所示:

    private <T> String myMethod(List<T> myList, MethodCaller<T> methodCaller) {
       // wherever you'd call your method, instead use methodCaller.callMethod() instead
    }
    

    现在调用你的方法,你会使用myMethod(list1, new Class1Caller())myMethod(list2, new Class2Caller())

    【讨论】:

      【解决方案3】:

      您也许可以使用继承来解决您的问题。为此,请让两个类都继承自一个公共基类(或接口)。然后制作一个 List 来存储子类的元素。将特定于每个类型的特殊逻辑存储在子类中。这是一个例子:

      class BaseClass {
        public void foo() {}
      }
      
      class ClassA extends BaseClass {
        public void foo() { /* ClassA logic here */ }
      }
      
      class ClassB extends BaseClass {
        public void foo() { /* ClassB logic here */ }
      }
      
      List<BaseClass> list = new ArrayList<BaseClass>();
      list.add(new ClassA());
      list.add(new ClassB());
      for(BaseClass element : list) {
        element.foo();
      }
      

      【讨论】:

      • 请注意,我已经有一段时间没有使用 Java,所以这可能不是 100% 有效的 Java 语法。如果没有,希望您可以根据自己的需要进行调整。
      【解决方案4】:

      虽然您可以使用.getClass()instanceofClass 参数,但这些往往会导致代码丑陋、脆弱。如果你能帮上忙,请不要去那里。

      方法#1

      最好的方法是使用抽象类或接口将所需的逻辑放入列表项中。列表对象将直接执行所需的任何操作。

      原来的帖子似乎表明这是不可能的,所以让我们继续方法2。

      方法#2

      使用函数参数。

      public <T> String myMethod(List<T> myList, Function<T, String> func)
      

      该函数将作用于列表中的每个项目,并返回您的方法可以处理的内容。

      方法#3

      使用包装类。您可以将每个列表项包装在知道如何处理它们的类中。包装类都将继承自一个基类,使您的方法看起来像这样:

      public String myMethod(List<Wrapper> myList)
      

      【讨论】:

        【解决方案5】:

        您可以使用 lambda 提取不同的调用并将其作为参数传递给您的方法:

        private <T> myMethod(List<? extends T> myList, Consumer<T> method) {
            for (T item : myList) {
                // some stuff
                method.accept(item);
            }
        }
        

        然后简单地这样称呼它:

        myMethod(list1, MyFirstClass::method1);
        myMethod(list2, MySecondClass::method2);
        

        这假设您正在调用 0-arg, void 方法。如果不是这种情况,您可以改用 lambda,和/或使用其他 standard functional interfaces 之一或创建自己的。

        请注意,这本质上是@jacobm's solution 的更简洁版本。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-30
          • 1970-01-01
          • 2023-03-24
          • 2018-10-23
          相关资源
          最近更新 更多