Вит*_*вич 25 java oop polymorphism overloading
我有一个集合(或列表或数组列表),我想在其中放置String值和double值.我决定使它成为一个对象集合并使用重载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?!?!?
}
}
Run Code Online (Sandbox Code Playgroud)
在测试中,似乎参数类型是在编译时而不是在运行时决定的.这是为什么?
这个问题与以下内容有关:
编辑:
好的,要调用的方法是在编译时决定的.是否有解决方法以避免使用instanceof运营商?
Dav*_*Far 19
这篇文章是秒voo的答案,并提供了有关后期绑定的替代方案的详细信息.
通用JVM仅使用单个调度:运行时类型仅考虑接收器对象; 对于方法的参数,考虑静态类型.使用方法表(类似于C++的虚拟表),使用优化的高效实现非常容易.您可以在HotSpot Wiki中找到详细信息.
如果您想为参数多次调度,请查看
this.resend(...)而不是super(...)调用封闭方法的最具体的重写方法;如果你想坚持使用Java,你可以
价值派发:
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'
}
}
Run Code Online (Sandbox Code Playgroud)
老问题但没有答案在 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"));
}
}
Run Code Online (Sandbox Code Playgroud)
现在,问题是:访问的类如何调用该visit()方法?
双分派实现的第二个分派依赖于接受被访问的类的“this”上下文。
因此,我们需要有一个accept()在方法Integer,String和Object类来执行本次调度:
public void accept(DisplayVisitor visitor){
visitor.visit(this);
}
Run Code Online (Sandbox Code Playgroud)
但不可能!访问的类是内置类:String, Integer, Object。
所以我们没有办法添加这个方法。
无论如何,我们不想添加它。
所以要实现双重调度,我们必须能够修改我们想要在第二次调度中作为参数传递的类。
因此,不是操作Object和List<Object>声明类型,我们将操作Foo和List<Foo>其中Foo类是保存用户值的包装器。
这是Foo界面:
public interface Foo {
void accept(DisplayVisitor v);
Object getValue();
}
Run Code Online (Sandbox Code Playgroud)
getValue()返回用户值。
它指定Object为返回类型,但 Java 支持协方差返回(从 1.5 版本开始),因此我们可以为每个子类定义更具体的类型以避免向下转换。
对象Foo
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;
}
}
Run Code Online (Sandbox Code Playgroud)
字符串富
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;
}
}
Run Code Online (Sandbox Code Playgroud)
整数富
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是访问子类的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());
}
}
Run Code Online (Sandbox Code Playgroud)
这是测试实现的示例代码:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
输出 :
字符串=一个字符串
字符串=另一个字符串
整数=1
对象=100
改进实施
实际的实现需要为我们想要包装的每个内置类型引入一个特定的包装类。正如所讨论的,我们没有选择操作双重调度。
但请注意,可以避免Foo子类中的重复代码:
private Integer value; // or String or Object
@Override
public Object getValue() {
return value;
}
Run Code Online (Sandbox Code Playgroud)
我们确实可以引入一个抽象泛型类来保存用户值并提供一个访问器:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在Foosublasses 声明更轻:
public class IntegerFoo extends Foo<Integer> {
public IntegerFoo(Integer integer) {
super(integer);
}
@Override
public void accept(DisplayVisitor v) {
v.visit(this);
}
}
public class StringFoo extends Foo<String> {
public StringFoo(String string) {
super(string);
}
@Override
public void accept(DisplayVisitor v) {
v.visit(this);
}
}
public class ObjectFoo extends Foo<Object> {
public ObjectFoo(Object value) {
super(value);
}
@Override
public void accept(DisplayVisitor v) {
v.visit(this);
}
}
Run Code Online (Sandbox Code Playgroud)
和test()方法应该被修改声明一个通配符类型(?为)Foo在类型List<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);
}
}
Run Code Online (Sandbox Code Playgroud)
事实上,如果真的需要,我们可以Foo通过引入java代码生成来进一步简化子类。
声明这个子类:
public class StringFoo extends Foo<String> {
public StringFoo(String string) {
super(string);
}
@Override
public void accept(DisplayVisitor v) {
v.visit(this);
}
}
Run Code Online (Sandbox Code Playgroud)
可以像声明一个类并在以下位置添加注释一样简单:
@Foo(String.class)
public class StringFoo { }
Run Code Online (Sandbox Code Playgroud)
Foo编译时处理的自定义注释在哪里。