簡體   English   中英

在重載方法中選擇方法時,使Java考慮最具體的類型

[英]Make Java consider the most specific type when choosing method among overloaded methods

我想基於對象的類型執行幾個操作,而不使用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因為我讀過它是不好的做法; 在這種情況下,它仍然是不好的做法?

我的答案是讓A,B和C類實現一個通用接口。

然后他們每個人都可以擁有自己的特定接口。

這樣,您可以為所有對象調用相同的方法 (從而避免重載方法),並且還可以根據對象的類型(即實例化它的類) 確保自定義功能

這個答案認為修改給定的類及其關系不是一種選擇。

有沒有辦法讓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)被調用。

使用訪客模式檢查我的第二個鏈接是否有缺點。

方法重載是一個編譯時多態,在你的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();
 }

考慮訪客設計模式http://en.wikipedia.org/wiki/Visitor_pattern 雖然它需要更改A,B,C,否則沒有其他辦法。

我會在這里使用instanceof 它簡單明了。

這並不像沒有使用instanceof硬規則 沒有硬規則:)很多使用instanceof可能是一個跡象,你可以改變一些東西,讓編譯器為你做更多的工作。 這是否真的值得做,需要逐個查看。 在你的情況下,你提到你無法改變有問題的類,所以它甚至不是一個選項。

    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();
                }

        }


    }

輸出: - ABC Base

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

看完上面的答案

我可以建議的一個解決方案是使用o.getClass和continue來檢查對象類型

然而,發布了許多更好的解決方 我完全同意@krisna kishor shetty說的話

從我的觀點來看,這不是使用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將引用包裝對象,如方法名稱所示。 這樣,您就可以獲得接近直接修改這些類的編碼模型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM