在重载方法中选择方法时,使Java考虑最具体的类型

时间:2013-08-09 14:01:47

标签: java polymorphism

我想根据对象的类型执行多个操作,而不使用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.accept(this)中的class A会导致调用函数visitor.accept(A o)
  • 我老实说反对instanceof,因为我读过它是不好的做法;在这种情况下,它仍然是不好的做法吗?

9 个答案:

答案 0 :(得分:11)

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

然后他们每个人都可以拥有自己特定的接口实现。

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

答案 1 :(得分:4)

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

  

有没有办法让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)被调用。

检查我的第二个链接是否使用访客模式。

答案 2 :(得分: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();
 }

答案 3 :(得分:2)

考虑访客设计模式http://en.wikipedia.org/wiki/Visitor_pattern。虽然它需要更改A,B,C,否则没有别的办法。

答案 4 :(得分:1)

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

不像硬规则不使用instanceof。没有硬性规则:) instanceof的大量使用可能表明您可以更改内容以使编译器为您做更多工作。这是否真的值得做,需要逐个查看。在你的情况下,你提到你无法改变有问题的类,所以它甚至都不是一个选项。

答案 5 :(得分:0)

    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 基

答案 6 :(得分:0)

现在没有使用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();
}        

答案 7 :(得分:0)

阅读上述答案后

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

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

答案 8 :(得分:0)

从我的观点来看,这不是使用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();
}

当然,我们的Adapter将引用包装对象,如方法名称所示。这样,您就可以获得接近直接修改这些类的编码模型。