2012-11-27 32 views
9

Điều này có vẻ giống như một câu hỏi mới, nhưng lần cuối cùng tôi làm việc với Java, ngôn ngữ không có generics. Tôi có một hệ thống phân cấp lớp (tên đã thay đổi để được như tổng quát càng tốt):Thành viên Java tĩnh trong một phân lớp được truy cập thông qua một phụ huynh chung

public abstract class AbstractBase { .... } 
public class ConcreateSubA extends AbstractBase { .... } 
public class ConcreateSubB extends AbstractBase { .... } 
... 
public class ConcreateSubZZ9PluralZAlpha extends AbstractBase { .... } 
... 

Tôi đang cố gắng để làm sạch một số mã di sản, và có một nơi mà một tấn trùng lặp lặp đi lặp lại có thể tất cả là yếu tố ra vào một thói quen thông qua Generics. (Tôi đang nghĩ đến Generics vì khi thói quen này được gọi, nó cần phải hoạt động trên chỉ là một trong các lớp bê tông.)

Các chàng thường xuyên như

public <Thing extends AbstractBase> void someFunc() 
{ 
    another_function_call (Thing.concreteSpecialToken); 
    // could also be 
    // another_function_call (Thing.concreteSpecialToken()); 
    // if methods are more feasible than fields 

    // Cannot use 
    // another_function_call (Thing().concreteSpecialToken()); 
    // because creating instances of these types is a Major Operation[tm] 
} 

Tôi đi đây hiểu về một dòng zillion , nhưng đó là phần quan trọng: someFunc() là loại tham số (nó thực sự có đối số nhưng không ai trong số họ là những điều nên không suy luận). Cuối cùng tôi cần lấy một mã thông báo đặc biệt và đây là nơi tôi bị mờ.

Mã thông báo là các chuỗi độc đáo rất lớn cho mỗi lớp cụ thể. Chúng dựa trên lớp, không dựa trên cá thể. Giá trị mã thông báo thực tế được khai báo là trường private static final trong mỗi lớp con.

Vì vậy, tôi cần sử dụng các phương thức/trường công khai của một lớp cơ sở để (cuối cùng) nhận được trường tĩnh riêng của một lớp con. Rõ ràng tôi không thể khai báo một phương thức abstract static trong cơ sở, bởi vì điều đó không có ý nghĩa gì cả. Nếu dữ liệu được dựa trên cá thể, thì điều này sẽ là tầm thường, với một bộ thu thập đa hình trong lớp cơ sở, nhưng các công cụ phân lớp là tĩnh.

Tôi cảm thấy như tôi đang thiếu một tính năng của Generics Java ở đây, nhưng tôi không thể sử dụng Thing.whatever() trừ khi whatever là một cái gì đó có thể khai báo trong lớp cơ sở trừu tượng. Tôi đang chạy lên chống lại một trong hai giới hạn của Java hoặc thiếu chuyên môn của tôi cố gắng thu hẹp khoảng cách. Một nỗ lực mà tôi thực hiện có vẻ đầy hứa hẹn cũng có rất nhiều sự sao chép mã tất cả các con đường trong hệ thống phân cấp lớp, xác định các phương thức trừu tượng với cùng một mã chính xác hơn ... đó là những gì generics được cho là để ngăn chặn!

+2

+1 cho 'ZZ9PluralZAlpha' :-) –

Trả lời

7

Tôi đang chạy lên chống lại một trong hai giới hạn của Java hoặc thiếu chuyên môn cố gắng thu hẹp khoảng cách.

Đó là giới hạn của Java, mặc dù IMO khá hợp lý. Về cơ bản bạn vẫn đang cố gắng sử dụng các thành viên tĩnh như thể chúng đa hình, và điều đó sẽ không hoạt động - Generics không giúp bạn ở đó.

Options:

  • Sử dụng phản chiếu ... nhưng hãy nhớ rằng loại chô bôi có nghĩa là bạn không thể có được tại Class cho Thing trừ khi bạn vượt qua nó một cách rõ ràng trong
  • Nếu bạn đã có một thể hiện của Thing anyway, chỉ cần biến nó thành thành viên trừu tượng, mà trong mỗi lần triển khai sẽ trả về giá trị của trường tĩnh
  • Tạo một loại hình riêng biệt chy mà sẽ sử dụng các thành viên dụ
3

Nếu tôi hiểu chính xác, bạn cần lớp bê tông của thông số loại. Cách thông thường để thực hiện nó là khai báo phương thức của bạn như thế này: public <T extends AbstractBase> void someFunc(Class<T> clazz)

Điều này tất nhiên có nghĩa là một tham số phụ cần được chuyển đến phương thức và bạn cần sử dụng sự phản chiếu để truy cập các trường tĩnh. đó là cách duy nhất.

Đạo đức của câu chuyện là generics và statics không đi cùng nhau tốt.

2

Một chút vụng về nhưng nếu someFunc mất một tham số Class<Thing> bạn có thể sử dụng phản ánh:

public <Thing extends AbstractBase> void someFunc(Class<Thing> clz) { 
    // exception handling omitted 
    Object whatever = clz.getDeclaredMethod("whatever").invoke(null); 

Nhưng bạn có thể có thể tận dụng tốt hơn đa hình bằng cách sử dụng các lớp lồng nhau, một cái gì đó giống như

public abstract class AbstractBase { 
    public static class Info { 
    public String getInfo() { 
     return "AbstractBase"; 
    } 
    } 
} 

public class ConcreteSubA extends AbstractBase { 
    public static final Info INFO = new Info() { 
    public String getInfo() { return "ConcreteSubA"; } 
    } 
} 

và có someFunc tham số AbstractBase.Info.

public <Thing extends AbstractBase> someFunc(AbstractBase.Info inf) { 
    String info = inf.getInfo(); 
} 

// call it as 
ConcreteSubB csb = someFunc(ConcreteSubB.INFO); 

Ý tưởng là mỗi lớp trong hệ thống phân cấp có một singleton dụ của Info giữ dữ liệu trước đây là tĩnh của nó.

+0

Đó là sự cố; tại thời điểm someFunc được gọi, không có thứ gì tồn tại. Nhưng tôi sẽ xem xét lại sự phản ánh, vì vậy cảm ơn bạn! –

+0

@TiStrga Tôi đã mở rộng một chút về cách tiếp cận thay thế, đưa dữ liệu vào một cá thể đơn lẻ của một lớp khác chứ không phải là số liệu thống kê trực tiếp. –

1

Nếu bạn muốn giữ lại các dấu hiệu như nó là, một lĩnh vực private static final, bạn có thể nhận được nó thông qua phản ánh:

public <Thing extends AbstractBase> void someFunc(Class<Thing> clz) 
{ 
    try { 
     Field field = clz.getField("concreteSpecialToken"); 
     field.setAccessible(true); 
     Object concreteSpecialToken = field.get(null); 
     another_function_call (concreteSpecialToken); 
    } catch (IllegalAccessException e) { 
     handle(e); 
    } catch (NoSuchFieldException e) { 
     handle(e); 
    } 
} 

Tại trang web cuộc gọi, bạn phải làm someFunc(ConcreateSubZZ9PluralZAlpha.class) . Nhưng tôi tự hỏi nếu bạn có thể làm điều đó, tại sao bạn không chỉ vượt qua đối tượng token như một tham số, như trong someFunc(ConcreateSubZZ9PluralZAlpha.concreteSpecialToken)? Hoặc thậm chí có thể di chuyển phương thức someFunc() tới chính lớp đó.

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