8

我想根据对象的类型执行几个操作,而不使用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因为我读过使用它是不好的做法;在这种情况下,这仍然是不好的做法吗?
4

9 回答 9

11

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

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

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

于 2013-08-09T14:06:37.190 回答
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总是一种不好的做法,它就不会作为语言的一部分存在。看到这个这个

我实际上是在尝试模拟访问者模式,但是看起来,访问者模式之所以起作用是因为双重调度,这使得在函数调用期间被“接受”的参数处于正确的类型,特别是 visitor.accept(this)在类 A 中导致函数 visitor.accept(A o) 被调用。

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

于 2013-08-09T14:14:18.977 回答
3

方法重载是一种编译时多态性,在您的for循环中,您已声明oObject,因此将始终调用 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();
 }
于 2013-08-09T14:05:30.170 回答
2

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

于 2013-08-09T14:09:30.513 回答
1

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

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

于 2013-08-09T14:53:08.813 回答
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();
                }

        }


    }

输出: - A B C 基础

于 2013-08-09T14:09:22.953 回答
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();
}        
于 2013-08-09T14:19:38.333 回答
0

看完上面的回答

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

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

于 2013-08-09T14:20:55.377 回答
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();
}

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

于 2013-08-09T14:51:30.143 回答