【问题标题】:Make Java consider the most specific type when choosing method among overloaded methods让Java在重载方法中选择方法时考虑最具体的类型
【发布时间】:2013-08-09 14:01:46
【问题描述】:

我想根据对象的类型执行几个操作,而不使用instanceof。起初我在考虑基于类型重载方法(如下所示),并认为也许 Java 会适当地选择方法(基于最具体的对象类)。

import java.util.ArrayList;
import java.util.List;


public class TestA {

    public static void main(String[] args)
    {
        List<Object> list = new ArrayList();
        list.add(new A());
        list.add(new B());
        list.add(new C());
        list.add(new Object());

        TestA tester = new TestA();

        for(Object o: list)
        {
            tester.print(o);
        }
    }

    private void print(A o)
    {
        System.out.println("A");
    }

    private void print(B o)
    {
        System.out.println("B");
    }

    private void print(C o)
    {
        System.out.println("C");
    }

    private void print(Object o)
    {
        System.out.println("Object");
    }
}

class A {

}

class B extends A {

}

class C {

}

输出是:

Object
Object
Object
Object

但是我想要的输出是:

A
B
C
Object
  • 有没有办法让Java根据参数对象的最具体类型来选择方法?
  • 如果没有,在没有instanceof 的帮助下,我可以为此类功能寻找哪些替代方案
  • 我实际上是在尝试模拟 visitor 模式,但是看起来,visitor 模式起作用的原因是由于双重调度,这使得被“接受”的参数在函数期间处于正确的类型调用,特别是,class A 中的visitor.accept(this) 导致函数visitor.accept(A o) 被调用。
  • 我真的反对instanceof,因为我读过使用它是不好的做法;在这种情况下,这仍然是不好的做法吗?

【问题讨论】:

  • 你的C 类不是扩展BA 吗?
  • 那么,如果没有instanceof 检查和适当的转换,恐怕你无法做到这一点。不过,让我再看看你的代码。
  • 你不能那样做。但看起来你有一个 XY 问题。你到底想达到什么目标?
  • 它调用print(Object) 因为你传递了Object
  • @PatriciaShanahan 我猜instanceof 最适合这种情况。谢谢你的回答。

标签: java polymorphism


【解决方案1】:

我的答案是让 A、B 和 C 类实现一个通用接口。

然后他们每个人都可以有自己的这个接口的具体实现。

这样,您可以为所有对象调用相同的方法(从而避免重载方法),并且还可以确保自定义功能基于对象的类型(即实例化它的类)。

【讨论】:

  • 这通常是一个很好的答案,但它涉及更改 A、B 和 C 的定义。OP 在评论中提到这样做不是他的选择。
  • @overthink,在这种情况下,使用实现公共接口的包装器扩展所有三个类,并使用这些包装器(/那个接口)而不是 A、B、C 对象。同样,如果 A、B 和/或 C 是​​最终类,则不是一个选项,否则可行。
【解决方案2】:

此答案认为修改给定类及其关系不是一种选择。

有没有办法让Java选择最基础的方法 参数对象的具体类型?

Java 编译器无法为您强制转换,因为这可能不是您想要的(在您的情况下,这就是您想要的,但也许其他人不想要这种行为,他们不会为他们提供解决方案)。由于您将Object 引用传递给print(),因此编译器将调用print(Object)

如果没有,我可以为此类功能寻找哪些替代方案, 没有instanceof的帮助

您可以将 Object 与其类型包装在一个包装类中,例如:

public class ObjectWrapper {
    private final Object object;
    private final int type;
}

这样您就可以安全地转换为Object 的类型,而无需使用instanceof。虽然这个恕我直言比简单地使用instanceof 更复杂,实际上它只会创建你自己的instanceof...

我真的反对 instanceof,因为我读过使用它是不好的 实践;在这种情况下,这仍然是不好的做法吗?

在编程中,没有什么是坏习惯。一切都取决于情况。在这种情况下,我认为您可以使用instanceof 并进行不安全的铸造,因为设计需要这样做。无论如何,如果instanceof 运算符存在于语言中,是因为它被使用了。如果instanceof总是是一种不好的做法,它就不会作为语言的一部分存在。请参阅thisthis

我实际上是在尝试模拟访问者模式,但是它 看来,使访问者模式起作用的原因是双重 dispatch,这使得被“接受”的参数是正确的 在函数调用期间输入,尤其是类中的 visitor.accept(this) A 导致函数 visitor.accept(A o) 被调用。

检查我的第二个链接,了解使用访问者模式的缺点。

【讨论】:

    【解决方案3】:

    方法重载是一种编译时多态性,在您的 for 循环中,您已将 o 声明为 Object,因此将始终调用 print(Object o)。

    解决方案: 使用动态多态性:

    class A{
    
      void print(){
         System.out.println('in A');
      }
    }
    
    class B{
    
      void print(){
         System.out.println('in B');
      }
    }
    

    和for循环

     for(Object o: list){
         o.print();
     }
    

    【讨论】:

    • 这个答案似乎很符合我的要求。但是有没有办法在不修改指定的类的情况下实现这个?
    • 恐怕不行,你需要在对象o上调用print()方法,而不是在普通方法中传递。
    • @Chad 您需要更改指定类中的代码。
    【解决方案4】:

    考虑访问者设计模式http://en.wikipedia.org/wiki/Visitor_pattern。虽然它需要对 A,B,C 进行更改,否则没有其他办法。

    【讨论】:

    • 这实际上是我正在研究的模式(如帖子中所述),但我试图修改类。
    • 那么你将不得不使用 instainceof :)
    • @EvgeniyDorofeev 不强制。请参阅我的答案以获得(坏)替代方案。
    【解决方案5】:

    我会在这里使用instanceof。简单明了。

    不使用instanceof 并不是有硬性规定。没有硬性规则 :) 大量使用 instanceof 可能表明您可以更改一些内容以使编译器为您做更多的工作。这是否真的值得做需要逐案考虑。在您的情况下,您提到您无法更改相关课程,因此它甚至不是一个选项。

    【讨论】:

      【解决方案6】:
          package com.ank.says;
          import java.util.ArrayList;
          import java.util.List;
      
          class Base{
              public void print(){
                  System.out.println("Base");
              }
      
          }
      
          class A extends Base{
              @Override
              public void print() {
                  System.out.println("A");
              }
          }
      
          class B extends Base{
              @Override
              public void print(){
                  System.out.println("B");
              }
          }
      
          class C extends Base{
              @Override
              public void print(){
                  System.out.println("C");
              }
          }
      
      
      
          public class TestPlayGround {
      
              public static void main(String[] args) throws ParseException {
                    List<Base> list = new ArrayList<Base>();
                      list.add(new A());
                      list.add(new B());
                      list.add(new C());
                      list.add(new Base());
      
                      TestPlayGround tester = new TestPlayGround();
      
                      for(Base o: list)
                      {
                          o.print();
                      }
      
              }
      
      
          }
      

      输出:- 一个 乙 C 基地

      【讨论】:

      • 嗯,你修改了他的代码,以至于它现在与原来的问题无关......
      • 我刚刚给出了一个方法。但你是对的,这可能不是最好的方法。
      【解决方案7】:

      看完以上回答

      我可以建议的一个解决方案是使用o.getClass 检查对象类型并继续

      但是发布了许多更好的解决方案。我完全同意@krisna kishor shetty 所说的

      【讨论】:

        【解决方案8】:

        现在不使用 instanceof 从我脑海中浮现的是:

        • 为所有人使用一个通用接口来共享一个通用方法:

        代码:

        public interface YourInterface {
            public void doStuff();
        }
        
        public class A implements YourInterface {
            @Override
            public doStuff() {
                System.out.print("A");
            }
        }
        
        public class B implements YourInterface {
         @Override
            public doStuff() {
                System.out.print("A");
            }
        }
        
        List<YourInterface> list = new ArrayList<YourInterface>();
            list.add(new A());
            list.add(new B());
        
        for(YourInterface e: list)
        {
            e.doStuff();
        }        
        
        • 使用 Enum 声明一个抽象方法,您拥有的每种类型都将实现该方法:

        代码:

        public enum YourEnum { 
            TYPE1 {
            @Override
            public void doStuff() {
                System.out.print("Type 1"); 
            },
        
            TYPE2 {
            @Override
            public void doStuff() {
                System.out.print("Type 2");         
            };
        
            public abstract void doStuff();
        }
        
        List<YourEnum> list = new ArrayList<YourEnum>();
            list.add(YourEnum.TYPE1);
            list.add(YourEnum.TYPE2);
        
        for(YourEnum e: list)
        {
            e.doStuff();
        }        
        

        【讨论】:

          【解决方案9】:

          在我看来,是否使用 instanceof 不是问题。问题是如何将这种情况所带来的影响降到最低。所以我会在这里推荐实例适配器,以防有几个操作需要处理。这样,您至少可以避免将这种复杂性传播到整个系统:

          public abstract class Adapter {
            public abstract void print();
          
            public static Adapter wrap(Object o) {
              if (o instanceof A) {
                return new AAdapter();
              }
              if (o instanceof B) {
                return new BAdapter();
              }
              return new ObjectAdapter():
          }
          
          public class AAdapter {
            public void print() {
              System.out.printLn("A");
            }
          }
          

          然后循环:

          for (Object o: things) {
            Adapter.wrap(o).print();
          }
          

          当然,正如方法名称所暗示的那样,我们的适配器将引用被包装的对象。这样你就可以得到一个接近直接修改这些类的编码模型。

          【讨论】:

            猜你喜欢
            • 2012-03-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多