Java中的重载和多重分派

Java中的重载和多重分派,java,oop,polymorphism,overloading,Java,Oop,Polymorphism,Overloading,我有一个集合(或列表或数组列表),我想在其中放置字符串值和双精度值。我决定让它成为一个对象集合,并使用重载ond多态性,但我做了一些错误的事情 我运行了一个小测试: public class OOP { void prova(Object o){ System.out.println("object"); } void prova(Integer i){ System.out.println("integer"); } void

我有一个集合(或列表或数组列表),我想在其中放置字符串值和双精度值。我决定让它成为一个对象集合,并使用重载ond多态性,但我做了一些错误的事情

我运行了一个小测试:

public class OOP {
    void prova(Object o){
        System.out.println("object");
    }

    void prova(Integer i){
    System.out.println("integer");
    }

    void prova(String s){
        System.out.println("string");
    }

    void test(){
        Object o = new String("  ");
        this.prova(o); // Prints 'object'!!! Why?!?!?
    }

    public static void main(String[] args) {
        OOP oop = new OOP();
        oop.test(); // Prints 'object'!!! Why?!?!?
    }
}
在测试中,参数类型似乎是在编译时决定的,而不是在运行时决定的。为什么呢

这个问题涉及:


编辑:


好的,要调用的方法在编译时决定。是否有一种方法可以避免使用
instanceof
操作符?

这不是多义性,您只是重载了一个方法,并使用对象类型的参数来调用它。当调用重载的方法时,Java会根据传递给函数的变量的类型来选择限制性最强的类型。它不使用实际实例的类型。

Java中的所有内容都是
对象
/Object(基本类型除外)。将字符串和整数存储为对象,然后在调用
prove
方法时,它们仍然被称为对象。您应该查看一下关键字的
实例


您想要的是双重的或更一般的,实际上是用其他语言实现的(我想到的是CommonLisp)


java没有它的主要原因大概是因为它会带来性能损失,因为重载解析必须在运行时完成,而不是在编译时完成。通常的解决方法是-非常丑陋,但事实就是这样。

这篇文章介绍了voo的答案,并给出了后期绑定的详细信息/替代方案

一般JVM只使用单一分派:运行时类型只考虑接收方对象;对于方法参数,考虑了静态类型。使用优化的高效实现非常容易(类似于C++的虚拟表)。您可以在中找到详细信息,例如

如果您想要为您的参数多次分派,请查看

  • 。但据我最近所知,它有一个过时的、缓慢的多重分派实现(参见示例),例如,没有缓存
  • ,但这与Java完全不同
  • ,它为Java提供了多个分派。此外,您可以使用
    • this.重新发送(…)
      而不是
      super(…)
      以调用封闭方法中最具体的重写方法
    • 值分派(下面的代码示例)
如果你想坚持使用Java,你可以

  • 通过在细粒度的类层次结构上移动重载方法来重新设计应用程序。第41项中给出了一个示例(明智地使用重载)
  • 使用一些设计模式,如策略、访问者、观察者。这些通常可以解决与多重分派相同的问题(即,在这些情况下,您可以使用多重分派为这些模式提供微不足道的解决方案)

价值分配:

class C {
  static final int INITIALIZED = 0;
  static final int RUNNING = 1;
  static final int STOPPED = 2;
  void m(int i) {
    // the default method
  }
  void m(int@@INITIALIZED i) {
    // handle the case when we're in the initialized `state'
  }
  void m(int@@RUNNING i) {
    // handle the case when we're in the running `state'
  }
  void m(int@@STOPPED i) {
    // handle the case when we're in the stopped `state'
  }
}

旧问题但没有答案提供了一个具体的Java解决方案,以干净的方式解决问题。
事实上,这不是一个简单但很有趣的问题。这是我的贡献。

好的,要调用的方法在编译时决定。有没有 避免使用instanceof运算符的变通方法

正如在优秀的@DaveFar答案中所说的,Java只支持单一分派方法。
在此分派模式中,编译器通过依赖已声明的参数类型而不是其运行时类型,将方法绑定为在编译后立即调用。

我有一个要放入的集合(或列表或数组列表) 字符串值和双精度值

为了以干净的方式解决问题并使用双重分派,我们必须对被操纵的数据进行抽象。
为什么?

下面是一个简单的访问者方法来说明这个问题:

public class DisplayVisitor {

    void visit(Object o) {
        System.out.println("object"));
    }

    void visit(Integer i) {
        System.out.println("integer");
    }

    void visit(String s) {
        System.out.println("string"));
    }

}
现在,问题是:被访问的类如何调用
visit()
方法?
双重分派实现的第二次分派依赖于接受访问的类的“this”上下文。
因此,我们需要在
Integer
String
Object
类中有一个
accept()
方法来执行第二次分派:

public void accept(DisplayVisitor visitor){
    visitor.visit(this);
}
但是不可能!访问的类是内置类:
字符串
整数
对象

因此,我们无法添加此方法。
不管怎样,我们不想再加上这一点。

因此,为了实现双重分派,我们必须能够修改要在第二次分派中作为参数传递的类。
因此,我们将操作
Foo
List
作为声明的类型,而不是操作
Object
List
,其中
Foo
类是保存用户值的包装器。

以下是
Foo
界面:

public interface Foo {
    void accept(DisplayVisitor v);
    Object getValue();
}
getValue()
返回用户值。
它将
Object
指定为返回类型,但Java支持协方差返回(自1.5版本以来),因此我们可以为每个子类定义更具体的类型,以避免降级。

ObjectFoo

public class ObjectFoo implements Foo {

    private Object value;

    public ObjectFoo(Object value) {
        this.value = value;
    }

    @Override
    public void accept(DisplayVisitor v) {
        v.visit(this);
    }

    @Override
    public Object getValue() {
        return value;
    }

}
StringFoo

public class StringFoo implements Foo {

    private String value;

    public StringFoo(String string) {
        this.value = string;
    }

    @Override
    public void accept(DisplayVisitor v) {
        v.visit(this);
    }

    @Override
    public String getValue() {
        return value;
    }

}
public class IntegerFoo implements Foo {

    private Integer value;

    public IntegerFoo(Integer integer) {
        this.value = integer;
    }

    @Override
    public void accept(DisplayVisitor v) {
        v.visit(this);
    }

    @Override
    public Integer getValue() {
        return value;
    }

}
IntegerFoo

public class StringFoo implements Foo {

    private String value;

    public StringFoo(String string) {
        this.value = string;
    }

    @Override
    public void accept(DisplayVisitor v) {
        v.visit(this);
    }

    @Override
    public String getValue() {
        return value;
    }

}
public class IntegerFoo implements Foo {

    private Integer value;

    public IntegerFoo(Integer integer) {
        this.value = integer;
    }

    @Override
    public void accept(DisplayVisitor v) {
        v.visit(this);
    }

    @Override
    public Integer getValue() {
        return value;
    }

}
下面是DisplayVisitor类访问
Foo
子类:

public class DisplayVisitor {

    void visit(ObjectFoo f) {
        System.out.println("object=" + f.getValue());
    }

    void visit(IntegerFoo f) {
        System.out.println("integer=" + f.getValue());
    }

    void visit(StringFoo f) {
        System.out.println("string=" + f.getValue());
    }

}
下面是测试实现的示例代码:

public class OOP {

    void test() {

        List<Foo> foos = Arrays.asList(new StringFoo("a String"),
                                       new StringFoo("another String"),
                                       new IntegerFoo(1),
                                       new ObjectFoo(new AtomicInteger(100)));

        DisplayVisitor visitor = new DisplayVisitor();
        for (Foo foo : foos) {
            foo.accept(visitor);
        }

    }

    public static void main(String[] args) {
        OOP oop = new OOP();
        oop.test();
    }
}
我们确实可以引入一个抽象泛型类,该类保存用户值并提供访问器:

public abstract class Foo<T> {

    private T value;

    public Foo(T value) {
        this.value = value;
    }

    public abstract void accept(DisplayVisitor v);

    public T getValue() {
        return value;
    }

}
并且应该修改
test()
方法,为
列表中的
Foo
类型声明一个通配符类型(

void test() {

    List<Foo<?>> foos = Arrays.asList(new StringFoo("a String object"),
                                      new StringFoo("anoter String object"),
                                      new IntegerFoo(1),
                                      new ObjectFoo(new AtomicInteger(100)));

    DisplayVisitor visitor = new DisplayVisitor();
    for (Foo<?> foo : foos) {
        foo.accept(visitor);
    }

}
可以简单到声明一个类并在上添加注释:

@Foo(String.class)
public class StringFoo { }
其中
Foo
是在编译时处理的自定义注释。

我支持您的答案(+1),
public class StringFoo extends Foo<String> {

    public StringFoo(String string) {
        super(string);
    }

    @Override
    public void accept(DisplayVisitor v) {
        v.visit(this);
    }

}
@Foo(String.class)
public class StringFoo { }