2013-05-20 35 views
5

Mã sau đây biên dịch và hoạt động tốt. Tôi định nghĩa một giao diện Builder, sau đó lớp CarBuilder được sử dụng để xử lý bất cứ điều gì liên quan đến một chiếc xe, và lớp BusBuilder được sử dụng để xử lý bất cứ điều gì liên quan đến một chiếc xe buýt. Xe hơi và xe buýt chia sẻ một lớp trừu tượng có tên là Xe. Mã này rất đơn giản. Mã chí đầu ra:Chúng ta có thể tự động gọi một phương thức giao diện Java với một tham số chung không?

do something to CREATE the Car 
do something to UPDATE the Car 
do something to CREATE the Bus 
do something to UPDATE the Bus 

Đây là mã ban đầu mà biên dịch:

public abstract class Vehicle { } 
public class Car extends Vehicle { } 
public class Bus extends Vehicle { } 

public interface Builder<V extends Vehicle> { 
    public V createVehicle(String spec); 
    public void updateVehicle(String spec, Vehicle vehicle); 
} 

public class CarBuilder implements Builder<Car> { 
    public Car createVehicle(String spec) { 
     Car car = new Car(); 
     System.out.println("do something to CREATE the Car"); 
     return car; 
    } 
    public void updateVehicle(String spec, Vehicle vehicle) { 
     Car car = (Car) vehicle; 
     System.out.println("do something to UPDATE the Car"); 
     return; 
    } 
} 

public class BusBuilder implements Builder<Bus> { 
    public Bus createVehicle(String spec) { 
     Bus bus = new Bus(); 
     System.out.println("do something to CREATE the Bus"); 
     return bus; 
    } 
    public void updateVehicle(String spec, Vehicle vehicle) { 
     Bus bus = (Bus) vehicle; 
     System.out.println("do something to UPDATE the Bus"); 
     return; 
    } 
} 

@Test 
public void main() { 
    Builder<? extends Vehicle> builder = null; 
    Vehicle vehicle = null; 

    builder = new CarBuilder(); 
    vehicle = builder.createVehicle("my original Car spec"); 
    builder.updateVehicle("my modified Car spec", vehicle); 

    builder = new BusBuilder(); 
    vehicle = builder.createVehicle("my original Bus spec"); 
    builder.updateVehicle("my modified Bus spec", vehicle); 
} 

Nhưng, tôi muốn làm cho mã của tôi mạnh mẽ hơn nữa, đánh máy. Tôi muốn thay đổi phương thức giao diện,

từ

public void updateVehicle(String spec, Vehicle vehicle); 

để

public void updateVehicle(String spec, V vehicle); 

Nói cách khác, tôi cố gắng sử dụng generic V thay vì lớp cơ sở Vehicle trong chữ ký của giao diện của tôi. Điều này buộc người triển khai của Builder phải "đóng" trên loại sản phẩm cụ thể (ví dụ: Car hoặc Bus, nhưng không phải là loại cơ sở Vehicle). CarBuilder chỉ được xử lý Car; BusBuilder chỉ nên xử lý Bus.

Mã này trở thành như sau:

public interface Builder<V extends Vehicle> { 
    public V createVehicle(String spec); 
    public void updateVehicle(String spec, V vehicle); 
} 

public class CarBuilder implements Builder<Car> { 
    public Car createVehicle(String spec) { 
     Car car = new Car(); 
     System.out.println("do something to CREATE the Car"); 
     return car; 
    } 
    public void updateVehicle(String spec, Car car) { 
     System.out.println("do something to UPDATE the Car"); 
     return; 
    } 
} 

public class BusBuilder implements Builder<Bus> { 
    public Bus createVehicle(String spec) { 
     Bus bus = new Bus(); 
     System.out.println("do something to CREATE the Bus"); 
     return bus; 
    } 
    public void updateVehicle(String spec, Bus bus) { 
     System.out.println("do something to UPDATE the Bus"); 
     return; 
    } 
} 

@Test 
public void main() { 
    Builder<? extends Vehicle> builder = null; 
    Vehicle vehicle = null; 

    builder = new CarBuilder(); 
    vehicle = builder.createVehicle("my original Car spec"); 
    builder.updateVehicle("my modified Car spec", vehicle); <== compilation error 

    builder = new BusBuilder(); 
    vehicle = builder.createVehicle("my original Bus spec"); 
    builder.updateVehicle("my modified Bus spec", vehicle); <== compilation error 
} 

Hai lỗi biên dịch - biên dịch Java sẽ không cho phép điều này vì "Phương pháp updateVehicle (String, chụp # 3-of kéo dài xe?) Trong các loại Builder không áp dụng cho các đối số (String, Vehicle) ".

Trong Java có cách nào để làm những gì tôi muốn thực hiện không? Trong C#, tôi có thể chỉ cần nhập biến của mình với var từ khóa, như var vehicle thay vì Vehicle vehicle. Tôi nghe nói rằng Java generics invocation là một quyết định thời gian biên dịch. Có một kỹ thuật để vượt qua điều này?

+0

Tại sao phương pháp cập nhật nội dung nào đó về xe buýt hoặc xe hơi không thuộc loại tương ứng? – thatidiotguy

+0

Làm cho cảm giác có. Một Builder được sử dụng phổ biến nhất để tạo một cá thể, chứ không phải để cập nhật nó. – vptheron

Trả lời

4

Có một thành ngữ, được gọi là a capture helper, giúp trong trường hợp như thế này.

Về bản chất, với tuyên bố của bạn là builder, tất cả trình biên dịch đều biết là vehicle là thứ mở rộng Vehicle.Hơn nữa, nó biết rằng Builder yêu cầu một số loại cụ thể của Vehicle được chuyển đến phương pháp updateVehicle() của nó. Trình biên dịch không thể rút ra suy luận rõ ràng đối với chúng tôi — rằng loại tương thích. Để làm điều đó, chúng ta phải loại bỏ ký tự đại diện bằng cách sử dụng một phương thức trợ giúp.

Ứng dụng thực tế vào mã thực không phải là hiển nhiên đối với việc khai thác thử nghiệm đơn giản của bạn. Nhưng, áp dụng nó trong bối cảnh này sẽ giống như sau. Hy vọng rằng ví dụ này sẽ đủ để minh họa ý tưởng và giúp bạn áp dụng nó vào mã thực.

public void main() 
{ 
    Builder<? extends Vehicle> builder; 
    Vehicle vehicle; 

    builder = new CarBuilder(); 
    vehicle = createAndUpdateHelper(builder, "my original Car spec", "my modified Car spec"); 

    builder = new BusBuilder(); 
    vehicle = createAndUpdateHelper(builder, "my original Bus spec", "my modified Bus spec"); 
} 

private static <V extends Vehicle> V createAndUpdateHelper(Builder<V> builder, String s1, String s2) 
{ 
    V v = builder.createVehicle(s1); 
    builder.updateVehicle(s2, v); 
    return v; 
} 
2

Những gì bạn đang cố gắng làm không có ý nghĩa gì: bạn muốn người xây dựng chỉ chấp nhận xe hơi hoặc xe buýt (điều này là tốt), nhưng sau đó bạn cấp cho họ một ví dụ về phương tiện. Bạn đang làm điều chính xác mà bạn đang cố gắng ngăn chặn: chấp nhận xe Class generic.

+0

Tôi đã phải nhập biến của tôi với 'Xe' vì không có cách nào khác để làm điều đó trong Java. Giống như tôi đã đề cập trong C# tôi có thể gõ nó với 'var' để bảo tồn kiểu thời gian chạy - biến' xe' của tôi tại thời gian chạy chứa hoặc là Ô tô hoặc Xe buýt. Tôi đoán nó đi xuống đến thực tế là quyết định triệu tập Java Generics được thiết kế để được thực hiện tại thời gian biên dịch vì vậy không có cơ sở khác để có được xung quanh nó. –

+0

Vâng, xem xét rằng bạn đang làm mới CarBuilder() tại dòng i và sau đó gọi nó để tạo ra một chiếc xe tại dòng i + 1 bạn một cách an toàn có thể cung cấp cho các loại xe để xe mới được tạo ra của bạn. – vptheron

+0

Trong dự án thực sự của tôi, cá thể Builder được tiêm sau đó tôi gọi create() Tôi không thể nhập nó vào Ô tô hoặc Xe buýt. –

0

Phương pháp đơn giản nhất là lọc đầu vào không khớp với đầu vào mong muốn hoặc mong muốn.

if(vehicle instanceof Car) return; 

Thực hiện triệt để hơn (và ít giòn) có thể được tạo bằng cách sử dụng enums để kiểm soát các khía cạnh khác nhau của xe (Loại, Màu). Sau đó, bạn có thể sử dụng một Trình tạo duy nhất có các nút chuyển để xử lý bất kỳ kết hợp nào của chúng. (Tôi có một số mã ví dụ được viết cho điều này, nếu bạn cần nó thì hãy gửi cho tôi một tin nhắn và tôi sẽ gửi cho bạn một bản sao)

+0

Nó sẽ làm việc có, nhưng ý định của ông là có một mã được đánh máy mạnh mẽ, không nhận được lỗi trong thời gian chạy (thay vì trả lại tôi sẽ ném một IllegalArgumentException hoặc một cái gì đó như thế). – vptheron

+0

Cũng nói vtheron. Cảm ơn vì đã trả lời. –

+0

@vtheron Tôi hoàn toàn đồng ý, nhưng nếu đơn giản là mục tiêu thì đó là cách dễ nhất để khắc phục các sự cố tiềm ẩn. Hãy cho tôi một thời điểm tôi đang viết ra cách tốt nhất (theo ý kiến ​​của tôi) để xử lý việc này ngay bây giờ. –

Các vấn đề liên quan