2010-07-02 7 views
54

Tôi có một lớp trừu tượng A xác định các phương thức trừu tượng. Điều này có nghĩa rằng, đối với một lớp được instanciable, tất cả các phương pháp trừu tượng phải được thực hiện.Làm thế nào tôi có thể buộc một Constructor được định nghĩa trong tất cả các lớp con của lớp trừu tượng của tôi

Tôi muốn tất cả các lớp con của mình triển khai một hàm tạo với 2 tham số là tham số.

Tuyên bố một hàm tạo đánh bại mục đích của tôi, vì tôi muốn hàm tạo được định nghĩa trong các lớp con và tôi không biết gì về việc triển khai. Hơn nữa tôi không thể tuyên bố một nhà xây dựng như là trừu tượng;

Có cách nào để thực hiện việc này không?

Ví dụ về những gì tôi muốn:

Cho phép nói rằng tôi đang định API của một lớp Matrix. Trong vấn đề của tôi, Matrix không thể thay đổi kích thước của chúng.

Để tạo Ma trận, tôi cần cung cấp kích thước của nó.

Do đó, tôi muốn tất cả những người triển khai của tôi cung cấp hàm tạo với kích thước làm tham số. Nhà xây dựng này được thúc đẩy bởi vấn đề, không phải bởi một mối quan tâm thực hiện. Việc thực hiện có thể làm bất cứ điều gì nó muốn với những điều này, với điều kiện là tất cả ngữ nghĩa của các phương thức được giữ lại.

Giả sử tôi muốn cung cấp triển khai cơ bản phương pháp invert() trong lớp trừu tượng của tôi. Phương pháp này sẽ tạo một ma trận mới với this kích thước ngược. Cụ thể hơn, vì nó được định nghĩa trong lớp trừu tượng, nó sẽ tạo ra một cá thể mới của cùng một lớp là this, sử dụng một hàm tạo có hai int. Vì nó không biết cá thể nó sẽ sử dụng sự phản chiếu (getDefinedConstructor) và tôi muốn một cách để bảo đảm rằng tôi sẽ nhận được nó và rằng nó sẽ có ý nghĩa cho việc thực hiện.

+0

Một giải pháp thanh lịch có thể được tìm thấy tại đây: http://stackoverflow.com/questions/6028526/java-force-an-extending-class –

Trả lời

48

Bạn không thể buộc chữ ký của hàm tạo trong lớp con của bạn - nhưng bạn có thể buộc nó phải đi qua một hàm tạo trong lớp trừu tượng của bạn lấy hai số nguyên. Các lớp con có thể gọi hàm khởi tạo đó từ một hàm tạo parameterless, đi qua các hằng số chẳng hạn. Đó là gần nhất bạn có thể đến mặc dù. Hơn nữa, như bạn nói, bạn không biết gì về việc thực hiện - vì vậy làm thế nào để bạn biết rằng nó thích hợp cho họ để có một nhà xây dựng đòi hỏi hai số nguyên? Điều gì nếu một trong số họ cần một String là tốt? Hoặc có thể nó có ý nghĩa cho nó để sử dụng một hằng số cho một trong những số nguyên đó.

Hình ảnh lớn hơn ở đây là gì - lý do tại sao bạn có muốn buộc chữ ký hàm tạo cụ thể trên các lớp con của mình không? (Như tôi đã nói, bạn có thể không thực sự làm này, nhưng nếu bạn giải thích lý do tại sao bạn muốn nó, một giải pháp có thể nói về nó.)

Một lựa chọn là phải có một giao diện riêng cho một nhà máy:

interface MyClassFactory 
{ 
    MyClass newInstance(int x, int y); 
} 

Sau đó, mỗi lớp con cụ thể của bạn là MyClass cũng sẽ cần một nhà máy biết cách tạo một phiên bản cho hai số nguyên. Mặc dù nó không thuận tiện lắm - và bạn vẫn cần tự xây dựng các bản thân các nhà máy. Một lần nữa, tình hình thực sự ở đây là gì?

+2

Cho phép nói rằng tôi đang xác định API của một lớp Ma trận. Trong vấn đề của tôi, Matrix không thể thay đổi kích thước của chúng. Để tạo Ma trận, tôi cần cung cấp kích thước của nó. Do đó, tôi muốn tất cả những người triển khai của tôi cung cấp hàm tạo với kích thước là tham số. Nhà xây dựng này được thúc đẩy bởi vấn đề, không phải bởi một mối quan tâm thực hiện. Việc thực hiện có thể làm bất cứ điều gì nó muốn với những điều này, với điều kiện là tất cả ngữ nghĩa của các phương thức được giữ lại. – dodecaplex

+0

@dodecaplex: Nhưng nếu bạn muốn tạo một bản 'FixedSizeMatrix' thực hiện * luôn luôn * 10 x 10 thì sao? Bạn không thể * gọi * constructors đa hình anyway, vậy tại sao bạn đang cố gắng hạn chế việc thực hiện? –

+1

Vậy thì việc triển khai sẽ không phù hợp với API của tôi ... Nếu nó có lý do chính đáng để làm điều đó, thì nó sẽ cung cấp một hàm tạo zero arg và một hàm tạo 2 args sẽ tăng một ngoại lệ nếu arg không phải là 10x10. Điều này có nghĩa là tôi vẫn có thể tạo một Ma trận trống của cùng một bản triển khai và có cùng kích thước (mà không biết triển khai hiệu quả), nhưng tôi sẽ có ngoại lệ nếu tôi cố gắng sử dụng triển khai này cho các ma trận không phải 10x10 . – dodecaplex

2

Nếu bạn cần xác định trong giao diện của mình, biểu diễn bên trong mà triển khai các lớp sẽ sử dụng, thì bạn chỉ làm sai. Vui lòng đọc về số encapsulationdata abstraction.

Nếu triển khai trừu tượng của bạn dựa trên các chi tiết triển khai nhất định, thì chúng thuộc về lớp trừu tượng đó. Có nghĩa là, lớp trừu tượng nên định nghĩa một hàm tạo cho phép nó khởi tạo trạng thái bên trong cần thiết để cho phép các phương thức trừu tượng làm việc.

Nói chung, các nhà xây dựng nhằm tạo ra một thể hiện của một lớp bằng cách cung cấp một số chi tiết về trạng thái ban đầu của cá thể đối tượng đó. Điều này không có nghĩa là cá thể đang được xây dựng nên sao chép một tham chiếu đến từng đối số riêng lẻ như thường là trường hợp trong hầu hết các phần mềm tôi thấy. Do đó, ngay cả khi Java đã cung cấp một cấu trúc để buộc thực hiện một số ký hiệu Constructor trên các lớp con, các lớp con đó có thể dễ dàng loại bỏ các đối số.

+1

Vâng, nếu tôi định nghĩa một điểm trong không gian thứ nguyên, tôi biết chắc chắn rằng điểm không thể tồn tại trong không gian thứ nguyên m (m! = N). Do đó, Dường như với tôi rằng kích thước của không gian là một thuộc tính INHERENT của điểm, bất kể việc thực hiện điểm. Theo như tôi có thể nói đây là đóng gói và trừu tượng dữ liệu. Nếu kích thước là NECESSARILY được biết cho mỗi trường hợp được tạo ra, có vẻ như tự nhiên để YÊU CẦU tất cả các triển khai để cung cấp một hàm tạo lấy đối số này. – dodecaplex

+0

@dodecaplex, Ahh, một điểm, ví dụ tuyệt vời. Giả sử bạn đang nói về một điểm 2 chiều. Vâng, chắc chắn, tôi có thể sử dụng một giá trị x và y để đại diện cho nó. Hoặc, tôi có thể sử dụng một giá trị radian/độ và một giá trị khoảng cách để đại diện cho nó. Chỉ một ví dụ. Có thể có những cách khác. Bạn chỉ nghĩ rằng bạn biết cách người ta có thể thực hiện. Dù sao, theo định nghĩa, nếu bạn đang cố gắng để thực thi một đại diện bạn không sử dụng trừu tượng dữ liệu. –

3

Bạn có thể thử một cái gì đó như dưới đây. Hàm khởi tạo sẽ ném một ngoại lệ nếu lớp triển khai thực hiện không có một hàm tạo với các đối số thích hợp.

Điều này thật ngớ ngẩn. So sánh OK và Xấu. Cả hai lớp đều giống nhau, ngoại trừ OK đáp ứng yêu cầu của bạn và do đó vượt qua kiểm tra thời gian chạy. Do đó, việc thực thi yêu cầu thúc đẩy công việc bận rộn phản tác dụng.

Giải pháp tốt hơn sẽ là một số loại Nhà máy.

abstract class RequiresConstructor 
{ 
    RequiresConstructor(int x, int y) throws NoSuchMethodException 
    { 
    super(); 
    System.out.println(this.getClass().getName()) ; 
    this.getClass(). getConstructor (int.class , int.class) ; 
    } 

    public static void main(String[] args) throws NoSuchMethodException 
    { 
    Good good = new Good (0, 0); 
    OK ok = new OK(); 
    Bad bad = new Bad(); 
    } 
} 

class Good extends RequiresConstructor 
{ 
    public Good(int x, int y) throws NoSuchMethodException 
    { 
    super(x, y) ; 
    } 
} 

class OK extends RequiresConstructor 
{ 
    public OK(int x, int y) throws NoSuchMethodException 
    { 
    super(x, y) ; 
    throw new NoSuchMethodException() ; 
    } 

    public OK() throws NoSuchMethodException 
    { 
    super(0, 0) ; 
    } 
} 

class Bad extends RequiresConstructor 
{ 
    public Bad() throws NoSuchMethodException 
    { 
    super(0, 0) ; 
    } 
} 
0

Có lớp trừu tượng có phương pháp trừu tượng có những gì bạn có cho thông số. Ví dụ:

public abstract void setSize(int rows,int columns); 
2

Một chút muộn, nhưng ...

Chỉ cần tạo một constructor mặc định trong lớp học của bạn mà luôn luôn gọi là siêu constructor. Trong hàm dựng mặc định này, bạn có thể kiểm tra tất cả các hàm tạo đã định nghĩa với sự phản chiếu trên đối tượng lớp riêng của nó (mà sau đó không phải là lớp siêu trừu tượng mà là lớp con cụ thể). Nếu hàm khởi tạo bạn muốn triển khai bị thiếu, hãy ném một ngoại lệ thời gian chạy.

Tôi không phải là người bạn lớn của phản ánh bởi vì nó có hương vị của hack qua cánh cửa trở lại, nhưng đôi khi nó giúp ...

Hãy nhìn vào ví dụ này:

import java.lang.reflect.Constructor; 

public abstract class Gaga { 
    public Gaga() { 
    boolean found = false; 
    try { 
     Constructor<?>[] constructors = getClass().getConstructors(); 
     for (Constructor<?> c : constructors) { 
     if (c.getParameterTypes().length==2) { 
      Class<?> class0 = c.getParameterTypes()[0]; 
      Class<?> class1 = c.getParameterTypes()[1]; 
      if ((class0.getName().equals("int") || class0.isAssignableFrom(Integer.class)) 
       && (class1.getName().equals("int") || class1.isAssignableFrom(Integer.class))) 
      found = true; 
     } 
     } 
    } catch (SecurityException e) 
    { 
     found = false; 
    } 

    if (!found) 
     throw new RuntimeException("Each subclass of Gaga has to implement a constructor with two integers as parameter."); 

    //... 
    } 

} 

Và một lớp kiểm tra:

public class Test { 
    private class Gaga1 extends Gaga { 
    public Gaga1() { this(0, 0); } 
    public Gaga1(int x, Integer y) { } 
    } 

    private class Gaga2 extends Gaga { 

    } 

    public static void main(String[] args) 
    { 
    new Gaga1(); 
    new Gaga1(1, 5); 
    new Gaga2(); 
    System.exit(0); 
    } 
} 

Trong chức năng chính, các đối tượng của Gaga1 sẽ được tạo, nhưng việc tạo Gaga2 sẽ ném ngoại lệ thời gian chạy.

Nhưng bạn không thể chắc chắn rằng hàm tạo này được gọi - bạn thậm chí không thể đảm bảo rằng nó đang làm những thứ bạn muốn.

Thử nghiệm này chỉ hữu ích nếu bạn đang làm việc với sự phản chiếu.