Có thể sử dụng toán tử instanceof trong câu lệnh switch không?


267

Tôi có một câu hỏi về việc sử dụng trường hợp chuyển đổi cho instanceofđối tượng:

Ví dụ: vấn đề của tôi có thể được sao chép trong Java:

if(this instanceof A)
    doA();
else if(this instanceof B)
    doB();
else if(this instanceof C)
    doC():

Làm thế nào nó sẽ được thực hiện bằng cách sử dụng switch...case?


6
Nếu bạn thực sự cảm thấy bạn cần một công tắc, bạn có thể băm tên lớp thành int và sử dụng nó, mặc dù vậy, hãy coi chừng các xung đột có thể xảy ra. Thêm nhận xét thay vì trả lời vì tôi không thích ý tưởng này thực sự được sử dụng. Có lẽ những gì bạn thực sự cần là mô hình khách truy cập.
vickirk

1
Kể từ java 7, bạn thậm chí có thể bật tên lớp đủ điều kiện để tránh các xung đột băm như @vickirk chỉ ra, nhưng nó vẫn xấu.
Mitja

Câu trả lời:


225

Đây là một kịch bản điển hình trong đó đa hình phụ giúp. Làm như sau

interface I {
  void do();
}

class A implements I { void do() { doA() } ... }
class B implements I { void do() { doB() } ... }
class C implements I { void do() { doC() } ... }

Sau đó, bạn có thể chỉ cần gọi do()vào this.

Nếu bạn không được tự do thay đổi A, BC, bạn có thể áp dụng mô hình khách truy cập để đạt được điều tương tự.


33
Mẫu khách truy cập có nghĩa là A, B và C phải triển khai giao diện với phương thức trừu tượng lấy Khách truy cập làm tham số đầu vào, nếu bạn không thể thay đổi A, B, C và không ai trong số họ thực hiện giao diện đó thì sao?
thermz

21
Nhận xét cuối cùng về mẫu khách truy cập là sai. Bạn vẫn sẽ cần làm cho A, B và C thực hiện giao diện.
Ben Thurley 27/1/2015

10
Đáng buồn là điều này không hoạt động nếu mã do () - Mã yêu cầu môi trường của máy chủ (tức là quyền truy cập vào các biến không có trong chính do ()).
mafu

2
@mafu Câu hỏi của OP là về việc gửi dựa trên loại. Nếu phương thức do () của bạn cần nhiều đầu vào hơn để gửi đi thì vấn đề của bạn là IMHO nằm ngoài phạm vi câu hỏi được thảo luận ở đây.
jmg

3
câu trả lời này giả định rằng bạn có thể sửa đổi các lớp A, B, C, trong khi tôi nghĩ vấn đề là làm thế nào để làm điều đó mà không sửa đổi A, B, C vì chúng có thể nằm trong thư viện phần thứ ba
cloudy_weather

96

nếu bạn hoàn toàn không thể mã hóa giao diện, thì bạn có thể sử dụng enum làm trung gian:

public A() {

    CLAZZ z = CLAZZ.valueOf(this.getClass().getSimpleName());
    switch (z) {
    case A:
        doA();
        break;
    case B:
        doB();
        break;
    case C:
        doC();
        break;
    }
}


enum CLAZZ {
    A,B,C;

}

thx, tôi cũng đã phải thực hiện một số thay đổi: 1) khởi tạo từng id enum với tham chiếu Class; 2) khẳng định tên đơn giản của lớp với id enum .toString (); 3) tìm enum thông qua tham chiếu Class được lưu trữ trên mỗi enum id. Tôi nghĩ đây cũng là an toàn.
Sức mạnh Bảo Bình

nếu this.getClass (). getSimpleName () không khớp với giá trị của CLAZZ, nó sẽ ném Ngoại lệ ... tốt hơn là bao quanh với một khối bắt thử và Ngoại lệ sẽ được coi là tùy chọn "mặc định" hoặc "khác" của công tắc
tetri

40

Chỉ cần tạo một Bản đồ trong đó lớp là khóa và chức năng, tức là lambda hoặc tương tự, là giá trị.

Map<Class,Runnable> doByClass = new HashMap<>();
doByClass.put(Foo.class, () -> doAClosure(this));
doByClass.put(Bar.class, this::doBMethod);
doByClass.put(Baz.class, new MyCRunnable());

// tất nhiên, cấu trúc lại cái này để chỉ khởi tạo một lần

doByClass.get(getClass()).run();

Nếu bạn cần kiểm tra Ngoại lệ hơn là triển khai FunctionalInterface, ném Ngoại lệ và sử dụng ngoại lệ đó thay vì Runnable.


3
Imho giải pháp tốt nhất, đặc biệt vì nó cho phép tái cấu trúc dễ dàng.
Feiteira

2
Nhược điểm duy nhất của giải pháp này là nó không thể sử dụng các biến cục bộ (phương thức) trong lambdas (giả sử có thể cần tất nhiên).
zapatero

1
@zapatero Bạn chỉ có thể thay đổi thành Hàm thay vì Runnable, chuyển thể hiện dưới dạng tham số, sau đó bỏ qua khi cần
Novaterata

Nâng cao; Đây là một trong số ít câu trả lời thực sự giúp OP thực hiện những gì anh ta yêu cầu (và vâng, thường có thể cấu trúc lại mã để không phải làm instanceof, và không, không may, kịch bản của tôi không phải là một trong những điều dễ dàng phù hợp với cái hộp đó ...)
Per Lundberg

@ SergioGutiérrez Cảm ơn. Chà, mẫu này chỉ cần thiết với một thư viện bên ngoài. Và thậm chí sau đó bạn chỉ có thể tạo một giao diện với triển khai bộ điều hợp thay thế, nhưng nó hữu ích khi bạn muốn DIFF hành vi, có thể nói, rõ ràng hơn. Tương tự như định tuyến API lưu loát và chú thích tôi cho rằng.
Novaterata

36

Chỉ trong trường hợp nếu ai đó sẽ đọc nó:

Giải pháp TỐT NHẤT trong java là:

public enum Action { 
    a{
        void doAction(...){
            // some code
        }

    }, 
    b{
        void doAction(...){
            // some code
        }

    }, 
    c{
        void doAction(...){
            // some code
        }

    };

    abstract void doAction (...);
}

Những lợi ích TUYỆT VỜI của mẫu đó là:

  1. Bạn chỉ cần làm như thế (KHÔNG có công tắc nào cả):

    void someFunction ( Action action ) {
        action.doAction(...);   
    }
  2. Trong trường hợp nếu bạn thêm Hành động mới gọi là "d", bạn PHẢI thực hiện phương thức doAction (...)

LƯU Ý: Mẫu này được mô tả trong Joshua's Bloch "Java hiệu quả (Phiên bản 2)"


1
đẹp! Là @Overrideyêu cầu trên mỗi thực hiện doAction()?
mateuscb

9
Làm thế nào đây là giải pháp "TỐT NHẤT"? Làm thế nào bạn sẽ quyết định actionsử dụng? Bằng một thể hiện bên ngoài mà gọi someFunction()với đúng action? Điều này chỉ cần thêm một mức độ gián tiếp.
PureSpider

1
Không, nó sẽ được thực hiện tự động trong thời gian chạy. Nếu bạn gọi someFunction (Action.a) thì a.doAction sẽ được gọi.
se.solovyev

11
Tôi không hiểu điều này. Làm thế nào bạn biết enum để sử dụng? Như @PureSpider đã nói, đây dường như chỉ là một cấp độ công việc khác phải làm.
James Manes

2
Thật đáng buồn khi bạn đã không đưa ra một ví dụ hoàn chỉnh , ví dụ như cách ánh xạ mọi Trường hợp của a, b hoặc C vào enum này. Tôi sẽ cố gắng sử dụng sơ thẩm cho Enum này.
Tom

21

Bạn không thể. Câu switchlệnh chỉ có thể chứa các casecâu lệnh là các hằng số thời gian biên dịch và đánh giá thành một số nguyên (Lên đến Java 6 và một chuỗi trong Java 7).

Những gì bạn đang tìm kiếm được gọi là "khớp mẫu" trong lập trình chức năng.

Xem thêm Tránh thể hiện trong Java


1
Không, trong hầu hết các ngôn ngữ chức năng, bạn không thể khớp mẫu trên các loại, chỉ trên các hàm tạo. Điều đó ít nhất đúng trong ML và Haskell. Trong Scala và OCaml có thể nhưng không phải là ứng dụng điển hình của khớp mẫu.
jmg

Chắc chắn, nhưng kiểm tra đối với các nhà xây dựng sẽ "tương đương" với kịch bản được mô tả ở trên.
Carlo V. Dango

1
Trong một số trường hợp, nhưng không nói chung.
jmg

Công tắc cũng có thể hỗ trợ enums.
Solomon Ucko

"Bạn không thể" nhìn vào một ngôn ngữ khác hiếm khi là một câu trả lời hữu ích.
L. Blanc

17

Như đã thảo luận trong các câu trả lời hàng đầu, phương pháp OOP truyền thống là sử dụng đa hình thay vì chuyển đổi. Thậm chí còn có một mô hình tái cấu trúc được ghi chép tốt cho thủ thuật này: Thay thế điều kiện bằng đa hình . Bất cứ khi nào tôi tiếp cận phương pháp này, tôi cũng muốn triển khai một đối tượng Null để cung cấp hành vi mặc định.

Bắt đầu với Java 8, chúng ta có thể sử dụng lambdas và generic để cung cấp cho chúng ta một thứ mà các lập trình viên chức năng rất quen thuộc: khớp mẫu. Đây không phải là một tính năng ngôn ngữ cốt lõi nhưng thư viện Javaslang cung cấp một triển khai. Ví dụ từ javadoc :

Match.ofType(Number.class)
    .caze((Integer i) -> i)
    .caze((String s) -> new BigDecimal(s))
    .orElse(() -> -1)
    .apply(1.0d); // result: -1

Đây không phải là mô hình tự nhiên nhất trong thế giới Java, vì vậy hãy thận trọng khi sử dụng nó. Mặc dù các phương thức chung sẽ giúp bạn không phải đánh máy giá trị khớp, nhưng chúng ta đang thiếu một cách tiêu chuẩn để phân tách đối tượng phù hợp như với các lớp trường hợp của Scala chẳng hạn.


9

Tôi biết điều này là rất muộn nhưng đối với độc giả tương lai ...

Cảnh giác với các cách tiếp cận ở trên chỉ dựa trên tên của lớp A , B , C ...:

Trừ khi bạn có thể đảm bảo rằng A , B , C ... (tất cả các lớp con hoặc người triển khai Base ) là cuối cùng thì các lớp con của A , B , C ... sẽ không bị xử lý.

Mặc dù cách tiếp cận if, otherif, otherif .. chậm hơn đối với số lượng lớn các lớp con / người thực hiện, nhưng nó chính xác hơn.


Thật vậy, bạn không bao giờ nên sử dụng đa hình (hay còn gọi là OOP)
Val

8

Thật không may, không thể ra khỏi hộp vì câu lệnh tình huống chuyển đổi mong đợi một biểu thức không đổi. Để khắc phục điều này, một cách sẽ là sử dụng các giá trị enum với các tên lớp, vd

public enum MyEnum {
   A(A.class.getName()), 
   B(B.class.getName()),
   C(C.class.getName());

private String refClassname;
private static final Map<String, MyEnum> ENUM_MAP;

MyEnum (String refClassname) {
    this.refClassname = refClassname;
}

static {
    Map<String, MyEnum> map = new ConcurrentHashMap<String, MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
        map.put(instance.refClassname, instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
}

public static MyEnum get(String name) {
    return ENUM_MAP.get(name);
 }
}

Với điều đó là có thể sử dụng câu lệnh chuyển đổi như thế này

MyEnum type = MyEnum.get(clazz.getName());
switch (type) {
case A:
    ... // it's A class
case B:
    ... // it's B class
case C:
    ... // it's C class
}

Tôi tin rằng cho đến khi vấn đề JEP 8213076 được thực hiện đầy đủ, đây là cách sạch nhất để có được một câu lệnh chuyển đổi dựa trên loại, mà không sửa đổi lớp mục tiêu.
Rik Schaaf

5

Không, không có cách nào để làm điều này. Tuy nhiên, những gì bạn có thể muốn làm là xem xét Đa hình như một cách để xử lý các loại vấn đề này.


5

Sử dụng các câu lệnh chuyển đổi như thế này không phải là cách hướng đối tượng. Thay vào đó bạn nên sử dụng sức mạnh của đa hình . Đơn giản chỉ cần viết

this.do()

Trước đây đã thiết lập một lớp cơ sở:

abstract class Base {
   abstract void do();
   ...
}

đó là lớp cơ sở cho A, BC:

class A extends Base {
    void do() { this.doA() }
}

class B extends Base {
    void do() { this.doB() }
}

class C extends Base {
    void do() { this.doC() }
}

@jmg suggeests ( stackoverflow.com/questions/5579309/switch-instanceof/ mẹo ) sử dụng giao diện thay vì lớp cơ sở trừu tượng. Điều đó có thể vượt trội trong một số rạp xiếc.
Raedwald

5

Điều này sẽ hoạt động nhanh hơn và có ý nghĩa trong trường hợp
- bạn có tương đối nhiều 'trường hợp'
- quá trình được thực hiện trong bối cảnh nhạy cảm về hiệu suất

public <T> T process(Object model) {
    switch (model.getClass().getSimpleName()) {
        case "Trade":
            return processTrade();
        case "InsuranceTransaction":
            return processInsuranceTransaction();
        case "CashTransaction":
            return processCashTransaction();
        case "CardTransaction":
            return processCardTransaction();
        case "TransferTransaction":
            return processTransferTransaction();
        case "ClientAccount":
            return processAccount();
        ...
        default:
            throw new IllegalArgumentException(model.getClass().getSimpleName());
    }
}

1
Điều này không giống như thực hiện một cá thể, vì điều này chỉ hoạt động nếu lớp triển khai được sử dụng để chuyển đổi, nhưng nó sẽ không hoạt động đối với các giao diện / lớp trừu tượng / siêu lớp
lifeso phi thường

vâng, đây không phải là
Mike

Một nỗ lực, nhưng ngoài nhận xét của @ lifeso phi thường, bạn cũng bỏ lỡ các loại an toàn mà bạn thường có, bởi vì câu trả lời này sử dụng chuỗi mã hóa cứng, thay vì tham chiếu lớp. Thật dễ dàng để tạo một lỗi đánh máy, đặc biệt là nếu bạn cần mở rộng chức năng này với các tên chính tắc đầy đủ nếu ở đây có bất kỳ sự trùng lặp nào trong các tên lớp với các tên gói khác nhau. Chỉnh sửa: lỗi chính tả (điều này chứng minh quan điểm của tôi)
Rik Schaaf

4

Bạn không thể chuyển đổi chỉ hoạt động với các kiểu byte, short, char, int, String và liệt kê (và các phiên bản đối tượng của nguyên thủy, nó cũng phụ thuộc vào phiên bản java của bạn, Chuỗi có thể được chỉnh sửa switchtrong java 7)


Bạn không thể bật Chuỗi trong Java 6. Và bạn không thể bật "phiên bản đối tượng của người nguyên thủy".
Lukas Eder

@Bozho Tôi đã nói nó phụ thuộc vào phiên bản java của bạn, trong Java 7 bạn có thể bật Chuỗi.
Tnem

@Lukas Eder kiểm tra thông số java của bạn mà bạn có thể
Tnem

4

Cá nhân tôi thích mã Java 1.8 sau:

    mySwitch("YY")
            .myCase("AA", (o) -> {
                System.out.println(o+"aa");
            })
            .myCase("BB", (o) -> {
                System.out.println(o+"bb");
            })
            .myCase("YY", (o) -> {
                System.out.println(o+"yy");
            })
            .myCase("ZZ", (o) -> {
                System.out.println(o+"zz");
            });

Sẽ xuất:

YYyy

Mã mẫu sử dụng Chuỗi nhưng bạn có thể sử dụng bất kỳ loại đối tượng nào, kể cả Class. ví dụ.myCase(this.getClass(), (o) -> ...

Cần đoạn trích sau:

public Case mySwitch(Object reference) {
    return new Case(reference);
}

public class Case {

    private Object reference;

    public Case(Object reference) {
        this.reference = reference;
    }

    public Case myCase(Object b, OnMatchDo task) {
        if (reference.equals(b)) {
            task.task(reference);
        }
        return this;
    }
}

public interface OnMatchDo {

    public void task(Object o);
}

4

Java bây giờ cho phép bạn chuyển đổi theo cách của OP. Họ gọi nó là khớp mẫu cho chuyển đổi. Nó hiện đang được soạn thảo nhưng nhìn thấy bao nhiêu công việc họ đã đưa vào thiết bị chuyển mạch gần đây tôi nghĩ rằng nó sẽ đi qua. Ví dụ được đưa ra trong JEP là

String formatted;
switch (obj) {
    case Integer i: formatted = String.format("int %d", i); break;
    case Byte b:    formatted = String.format("byte %d", b); break;
    case Long l:    formatted = String.format("long %d", l); break;
    case Double d:  formatted = String.format("double %f", d); break;
    case String s:  formatted = String.format("String %s", s); break
    default:        formatted = obj.toString();
}  

hoặc sử dụng cú pháp lambda của họ và trả về một giá trị

String formatted = 
    switch (obj) {
        case Integer i -> String.format("int %d", i)
        case Byte b    -> String.format("byte %d", b);
        case Long l    -> String.format("long %d", l); 
        case Double d  -> String.format("double %f", d); 
        case String s  -> String.format("String %s", s); 
        default        -> obj.toString();
    };

dù bằng cách nào họ đã làm những thứ tuyệt vời với công tắc.


3

Nếu bạn có thể thao tác giao diện chung, bạn có thể thêm vào một enum và mỗi lớp trả về một giá trị duy nhất. Bạn sẽ không cần thể hiện hoặc mẫu khách truy cập.

Đối với tôi, logic cần có trong văn bản trong câu lệnh switch chứ không phải chính đối tượng. Đây là giải pháp của tôi:

ClassA, ClassB, and ClassC implement CommonClass

Giao diện:

public interface CommonClass {
   MyEnum getEnumType();
}

Enum:

public enum MyEnum {
  ClassA(0), ClassB(1), ClassC(2);

  private int value;

  private MyEnum(final int value) {
    this.value = value;
  }

  public int getValue() {
    return value;
  }

Impl:

...
  switch(obj.getEnumType())
  {
    case MyEnum.ClassA:
      ClassA classA = (ClassA) obj;
    break;

    case MyEnum.ClassB:
      ClassB classB = (ClassB) obj;
    break;

    case MyEnum.ClassC:
      ClassC classC = (ClassC) obj;
    break;
  }
...

Nếu bạn đang dùng java 7, bạn có thể đặt các giá trị chuỗi cho enum và khối trường hợp chuyển đổi vẫn sẽ hoạt động.


Trường valuenày là dư thừa nếu bạn chỉ muốn phân biệt các hằng số enum - bạn có thể sử dụng các hằng số trực tiếp (như bạn làm).
dùng905686

2

Còn cái này thì sao ?

switch (this.name) 
{
  case "A":
    doA();
    break;
  case "B":
    doB();
    break;
  case "C":
    doC();
    break;
  default:
    console.log('Undefined instance');
}

3
Nên chỉ ra rằng điều này chỉ hoạt động trên Java 7. Và bạn phải gọi this.getSimpleName()Không chắc chắn liệu poster có bị nhầm lẫn với JS hay không (vâng, anh ấy đang sử dụng console, hehe).
pottaisco

5
Điều này có một vấn đề rơi ra khỏi tính minh bạch tham chiếu mã nguồn. Đó là, IDE của bạn sẽ không thể đảm bảo tính toàn vẹn tham chiếu. Giả sử bạn muốn đổi tên của bạn. Sự phản ánh là xấu xa.
Val

1
Không phải là một ý tưởng tuyệt vời. Tên lớp không phải là duy nhất nếu bạn có nhiều trình nạp lớp.
Doradus

Phá vỡ khi nói đến nén mã (→ ProGuard)
Matthias Rrid

1

Tôi nghĩ rằng có những lý do để sử dụng một tuyên bố chuyển đổi. Nếu bạn đang sử dụng xText tạo Mã có lẽ. Hoặc một loại khác của các lớp tạo EMF.

instance.getClass().getName();

trả về một chuỗi của tên thực hiện lớp. tức là: org.eclipse.emf.ecore.util.EcoreUtil

instance.getClass().getSimpleName();

trả về sự hồi phục đơn giản, tức là: EcoreUtil


Bạn không thể sử dụng nó trong switchkhi caseđiều kiện bởi vì nó không phải là giá trị không đổi
B-Gangster

1

Nếu bạn cần "chuyển đổi" thông qua loại lớp của đối tượng "này", câu trả lời này là https://stackoverflow.com/a/5579385/2078368 tốt nhất

Nhưng nếu bạn cần áp dụng "chuyển đổi" cho bất kỳ biến nào khác. Tôi sẽ đề nghị một giải pháp khác. Xác định giao diện sau:

public interface ClassTypeInterface {
    public String getType();
}

Thực hiện giao diện này trong mỗi lớp bạn muốn "chuyển đổi". Thí dụ:

public class A extends Something implements ClassTypeInterface {

    public final static String TYPE = "A";

    @Override
    public String getType() {
        return TYPE;
    }
}

Sau đó, bạn có thể sử dụng nó theo cách sau:

switch (var.getType()) {
    case A.TYPE: {
        break;
    }
    case B.TYPE: {
        break;
    }
    ...
}

Điều duy nhất bạn nên quan tâm - giữ "các loại" duy nhất trên tất cả các lớp triển khai ClassTypeInterface. Đó không phải là một vấn đề lớn, bởi vì trong bất kỳ giao lộ nào, bạn sẽ nhận được lỗi thời gian biên dịch cho câu lệnh "trường hợp chuyển đổi".


Thay vì sử dụng String cho TYPE, bạn có thể sử dụng enum và tính duy nhất được đảm bảo (như được thực hiện trong câu trả lời này ). Tuy nhiên, với bất kỳ cách tiếp cận nào, bạn sẽ phải cấu trúc lại ở hai nơi khi bạn đổi tên.
dùng905686

@ user905686 đổi tên là gì? Trong ví dụ hiện tại, loại "A" được định nghĩa bên trong lớp Something để giảm thiểu số lượng mã. Nhưng trong cuộc sống thực, rõ ràng bạn nên xác định nó bên ngoài (ở một số điểm chung) và không có bất kỳ vấn đề nào với việc tái cấu trúc thêm.
Serge Krivenkov

Ý tôi là đổi tên lớp A. Tái cấu trúc tự động có thể không bao gồm biến TYPE = "A"khi đổi tên. Đặc biệt nếu nó nằm ngoài lớp tương ứng, người ta cũng có thể quên nó khi thực hiện thủ công. IntelliJ thực sự cũng tìm thấy sự xuất hiện của tên lớp trong chuỗi hoặc nhận xét nhưng đó chỉ là tìm kiếm văn bản (thay vì nhìn vào cây cú pháp) và do đó bao gồm cả dương tính giả.
dùng905686

@ user905686 nó chỉ là một ví dụ, để hình dung ý tưởng. Không sử dụng Chuỗi cho các định nghĩa kiểu trong dự án thực, khai báo một số chủ sở hữu lớp MyTypes với các hằng số nguyên (hoặc enum) và sử dụng chúng trong các lớp triển khai ClassTypeInterface.
Serge Krivenkov

1

Đây là một cách chức năng để thực hiện nó trong Java 8 bằng cách sử dụng http://www.vavr.io/

import static io.vavr.API.*;
import static io.vavr.Predicates.instanceOf;
public Throwable liftRootCause(final Throwable throwable) {
        return Match(throwable).of(
                Case($(instanceOf(CompletionException.class)), Throwable::getCause),
                Case($(instanceOf(ExecutionException.class)), Throwable::getCause),
                Case($(), th -> th)
        );
    }

1

Mặc dù không thể viết một câu lệnh chuyển đổi, nhưng có thể phân nhánh để xử lý cụ thể cho từng loại đã cho. Một cách để làm điều này là sử dụng cơ chế điều phối kép tiêu chuẩn. Một ví dụ mà chúng tôi muốn "chuyển đổi" dựa trên loại là trình ánh xạ Jersey Exception nơi chúng tôi cần ánh xạ vô số ngoại lệ cho các phản hồi lỗi. Mặc dù đối với trường hợp cụ thể này, có lẽ có một cách tốt hơn (nghĩa là sử dụng phương pháp đa hình để dịch từng ngoại lệ thành phản hồi lỗi), sử dụng cơ chế điều phối kép vẫn hữu ích và thiết thực.

interface Processable {
    <R> R process(final Processor<R> processor);
}

interface Processor<R> {
    R process(final A a);
    R process(final B b);
    R process(final C c);
    // for each type of Processable
    ...
}

class A implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class B implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

class C implements Processable {
    // other class logic here

    <R> R process(final Processor<R> processor){
        return processor.process(this);
    }
}

Sau đó, bất cứ khi nào cần "chuyển đổi", bạn có thể làm như sau:

public class LogProcessor implements Processor<String> {
    private static final Logger log = Logger.for(LogProcessor.class);

    public void logIt(final Processable base) {
        log.info("Logging for type {}", process(base));
    }

    // Processor methods, these are basically the effective "case" statements
    String process(final A a) {
        return "Stringifying A";
    }

    String process(final B b) {
        return "Stringifying B";
    }

    String process(final C c) {
        return "Stringifying C";
    }
}

Điều này trông rất giống với mẫu Khách truy cập, đã được thảo luận trong câu trả lời này: stackoverflow.com/a/5579385
typeracer

0

Tạo một Enum với tên Class.

public enum ClassNameEnum {
    A, B, C
}

Tìm tên lớp của đối tượng. Viết một trường hợp chuyển đổi trên enum.

private void switchByClassType(Object obj) {

        ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName());

        switch (className) {
            case A:
                doA();
                break;
            case B:
                doB();
                break;
            case C:
                doC();
                break;
        }
    }
}

Hi vọng điêu nay co ich.


2
Ngược lại với cách tiếp cận này trong đó việc ghép nối giữa các hằng số enum và các lớp được thực hiện rõ ràng, bạn thực hiện khớp nối theo tên lớp. Điều này sẽ phá vỡ mã của bạn khi bạn chỉ đổi tên một trong các hằng số enum hoặc lớp trong khi cách tiếp cận khác vẫn hoạt động.
dùng905686

0

Khung mô hình hóa Eclipse có một ý tưởng thú vị cũng xem xét tính kế thừa. Khái niệm cơ bản được định nghĩa trong giao diện Switch : chuyển đổi được thực hiện bằng cách gọi phương thức doSwitch .

Điều thực sự thú vị là việc thực hiện. Đối với mỗi loại sở thích, một

public T caseXXXX(XXXX object);

phương thức phải được thực hiện (với việc thực hiện mặc định trả về null). Việc triển khai doSwitch sẽ cố gắng gọi al các phương thức caseXXX trên đối tượng cho tất cả hệ thống phân cấp kiểu của nó. Một cái gì đó trong dòng:

BaseType baseType = (BaseType)object;
T result = caseBaseType(eAttribute);
if (result == null) result = caseSuperType1(baseType);
if (result == null) result = caseSuperType2(baseType);
if (result == null) result = caseSuperType3(baseType);
if (result == null) result = caseSuperType4(baseType);
if (result == null) result = defaultCase(object);
return result;

Khung thực tế sử dụng một id số nguyên cho mỗi lớp, vì vậy logic thực sự là một chuyển đổi thuần túy:

public T doSwitch(Object object) {
    return doSwitch(object.class(), eObject);
}

protected T doSwitch(Class clazz, Object object) {
    return doSwitch(getClassifierID(clazz), object);
}

protected T doSwitch(int classifierID, Object theObject) {
    switch (classifierID) {
    case MyClasses.BASETYPE:
    {
      BaseType baseType = (BaseType)object;
      ...
      return result;
    }
    case MyClasses.TYPE1:
    {
      ...
    }
  ...

Bạn có thể xem xét một triển khai hoàn chỉnh của ECoreSwitch để có được ý tưởng tốt hơn.


-1

thậm chí còn có một cách đơn giản hơn để mô phỏng cấu trúc chuyển đổi sử dụng thể hiện, bạn thực hiện điều này bằng cách tạo một khối mã trong phương thức của bạn và đặt tên cho nó bằng nhãn. Sau đó, bạn sử dụng if cấu trúc để mô phỏng các báo cáo trường hợp. Nếu một trường hợp là đúng thì bạn sử dụng break LABEL_NAME để thoát khỏi cấu trúc chuyển đổi tạm thời của bạn.

        DEFINE_TYPE:
        {
            if (a instanceof x){
                //do something
                break DEFINE_TYPE;
            }
            if (a instanceof y){
               //do something
                break DEFINE_TYPE;
            }
            if (a instanceof z){
                // do something
                break DEFINE_TYPE;
            }
        }

Làm thế nào là tốt hơn so với if... else ifmã được đưa ra bởi OP?
typeracer

Chỉ cần giải thích về nhận xét trước đây của tôi: những gì bạn đề xuất về cơ bản là thay thế if... else ifbằng các câu lệnh "goto", đó là cách sai để thực hiện luồng điều khiển trong các ngôn ngữ như Java.
typeracer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.