Tham số của hàm Java có thể yêu cầu giao diện tùy chọn không?


8

Tôi có hai giao diện trong Java (phiên bản 8) rất giống nhau. Tôi không thể thay đổi giao diện và không thể thay đổi các lớp thực hiện chúng.

public interface A {
    int get();
}

public interface B {
    int get();
    int somethingelse();
}

Bây giờ tôi có một chức năng mà việc thực hiện của nó phù hợp với cả hai giao diện (gần như). Tôi muốn nó làm một cái gì đó như thế:

public int foo((A | B) p) {
    int ret = 0;
    if (p instanceof B) {
        ret = p.somthingelse();
    }
    return ret + p.get();
}

Tôi không muốn sử dụng kiểm tra vì chức năng này nằm trong hệ thống chính của chương trình của tôi. Tôi muốn nó có hiệu suất tốt. Có thể làm điều này trong Java?

Biên tập:

Một giải pháp đơn giản sẽ là sao chép / dán foo()và thực hiện nó khác nhau cho mỗi giao diện. Nhưng trong thực tế foo()và các giao diện dài hơn thế và tôi đang cố gắng tránh sao chép mã.


2
A và B không có gì chung. Vì vậy, hãy tạo hai phương thức foo (): một phương pháp lấy B và một phương pháp lấy A. Ngoài ra, đừng đưa ra giả định về nguyên nhân của vấn đề hiệu suất cho đến khi bạn gặp vấn đề về hiệu suất và bạn đã xác minh giả định này bằng các phép đo .
JB Nizet

@JBNizet A và B có get()điểm chung. Đây là một ví dụ đơn giản. Trong thực tế, chúng có nhiều chức năng chung.
Liran Funaro

1
Không, họ không. B không mở rộng A. Vì vậy, họ tình cờ có một phương thức có cùng chữ ký và kiểu trả về. Nếu mọi B được coi là A, thì B nên gia hạn A.
JB Nizet

1
@ user2478398 Bạn đã xem câu trả lời của ernest_k chưa? Tôi nghĩ rằng đó là những gì bạn đang nói về.
Liran Funaro

1
@LiranFunaro Nếu họ (cả hai giao diện) có điểm chung, tại sao không trích xuất nó cho cha mẹ và tiếp tục mở rộng nó? Ngoài ra: publicaccessor là dự phòng cho các phương thức giao diện. Và bạn có nghĩa là instanceofthay vì implements?
Naman

Câu trả lời:


4

Thật không may, không có cách nào để tạo ra mối quan hệ hồi tố giữa hai loại không liên quan.

Nếu bạn có thể thay đổi foo()và các yêu cầu của nó, bạn có thể sử dụng các giao diện chức năng khớp với các chữ ký mà bạn gọi bên trong foo. Tôi đang sử dụng IntSupplierở đây, với các biểu thức lambda tương ứng bằng cách sử dụng các triển khai cụ thể của AB.

Nói rằng bạn có những triển khai này:

class AImpl implements A {
     //implementation
}
class BImpl implements B {
     //implementation
}

Bạn có thể thay đổi foothành một cái gì đó như:

public int foo(IntSupplier get, IntSupplier somethingElse) {
    int ret = 0;
    if (somethingElse != null) {
        ret = somethingElse.getAsInt();
    }
    return ret + get.getAsInt();
}

Và gọi nó theo cách này:

A a = new AImpl();
B b = new BImpl();

int result = this.foo(a::get, b::somethingelse);

Giải pháp thú vị! Điều này có thể được mở rộng thành một giải pháp không yêu cầu thay đổi chữ ký của foo. Chỉ cần viết hai triển khai foo()cho mỗi giao diện và sau đó gọi cho bạn foo()với các nhà cung cấp phù hợp. Bạn có muốn thêm điều này hoặc bạn muốn tôi chỉnh sửa câu trả lời của bạn?
Liran Funaro

1
@LiranFunaro Phải. Tôi đã bị giới hạn bởi các ràng buộc rằng iflogic phải ở bên trong foo(). Nhưng vâng, bạn chắc chắn có thể làm điều đó nếu nó phù hợp.
ernest_k

1
Xin lỗi, nhưng có rất ít mà tôi có thể liên quan đến câu hỏi và IntSuppliergợi ý trong câu trả lời này. Ngoài ra, BImpl(hoặc đơn giản B) không thể được sử dụng như là một FunctionalInterface, được cung cấp nhiều hơn một phương thức trừu tượng. Hơn nữa, từ một bình luận , tuyên bố " hai loại không liên quan " không đúng. Nitpick: IntSupplier getkhông được sử dụng trong mã của bạn.
Naman

1
@ Naman Cảm ơn bạn đã xem. Bkhông được sử dụng như một giao diện chức năng. b:somethingelselà một tham chiếu phương thức IntSuppliertrong trường hợp này (Tôi khuyên bạn nên biên dịch mã). Và vâng, ABlà hai loại hoàn toàn không liên quan. Và về giải pháp tổng thể, tôi nghĩ rằng tôi hiểu rằng bạn thấy nó hơi khó xử. Tôi có thể cho rằng có những cách tiếp cận tốt hơn, nhưng không nhiều cách xa lánh việc tái cấu trúc mà OP không đủ khả năng.
ernest_k

3

Cách duy nhất tôi có thể tưởng tượng là thế này:

public int foo(A p) {
    return internal_foo(p);
}

public int foo(B p) {
    return internal_foo(p);
}

private int internal_foo(Object p) {
    if (p instanceof A) {
        return ((A)p).get();
    }
    if (p instanceof B) {
        B b = (B)p;
        ret = b.somthingelse();
        return ret + b.get();
    }
    throw new ClassCastException("Wrong type");
}

Cảm ơn. Vấn đề với điều này là việc thực hiện chức năng lâu hơn thế. Vì vậy, tôi sẽ phải sao chép / dán rất nhiều mã, đó là điều tôi đang cố gắng tránh :)
Liran Funaro

2

Afaik, Java không hỗ trợ điều này trực tiếp, nhưng bạn có thể tự tạo một "trình bao bọc kiểu liên kết". Một cái gì đó như thế này:

abstract class AorB {
    public static AorB wrap(A a) {
        return new AWrapper(a);
    }

    public static AorB wrap(B b) {
        return new BWrapper(b);
    }

    abstract int get();
    abstract int somethingElse();
}

class AWrapper extends AorB {
    private final A a;

    AWrapper(A a) {
        this.a = a;
    }

    @Override
    int get() {
        return a.get();
    }

    @Override
    int somethingElse() {
        return 0;
    }
}

class BWrapper extends AorB {
    private final B b;

    BWrapper(B b) {
        this.b = b;
    }

    @Override
    int get() {
        return b.get();
    }

    @Override
    int somethingElse() {
        return b.somethingElse();
    }
}

// and then

public int foo(A a) {
    return fooImpl(AorB.wrap(a));
}

public int foo(B b) {
    return fooImpl(AorB.wrap(b));
}

int fooImpl(AorB p) {
    return p.get() + p.somethingElse();
}

Bạn cũng có thể thêm một số chức năng kiểm tra như hasSomethingElse(), nếu không có giá trị mặc định phù hợp để trả về, như 0ở trên ..


Tôi cũng sẽ chấp nhận câu trả lời này nếu tôi có thể chấp nhận hai câu trả lời.
Liran Funaro
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.