2009-12-16 35 views
23

Tôi hiểu rằng cả lớp trừu tượng lẫn giao diện đều không thể chứa một phương thức vừa là abstract and static vì các vấn đề mơ hồ, nhưng có cách giải quyết khác không?Java static static Workaround

Tôi muốn có lớp trừu tượng hoặc giao diện yêu cầu đưa vào phương thức tĩnh trong tất cả các lớp mở rộng/triển khai lớp/giao diện này. Có cách nào để làm điều này trong Java? Nếu không, đây có thể là rơm cuối cùng của tôi với Java ...

EDIT 1: Bối cảnh của vấn đề này là tôi có một loạt các lớp, gọi chúng là Stick, Ball và Toy bây giờ, có một bó các mục trong cơ sở dữ liệu. Tôi muốn tạo một siêu lớp/giao diện được gọi là Có thể tìm nạp được yêu cầu một phương thức tĩnh getFetchables() trong mỗi lớp bên dưới nó. Lý do các phương thức trong Stick, Ball và Toy phải là tĩnh bởi vì chúng sẽ nói chuyện với một cơ sở dữ liệu để lấy tất cả các mục trong cơ sở dữ liệu cho mỗi lớp.

CHỈNH SỬA 2: Đối với những người nói bạn không thể làm điều này bằng bất kỳ ngôn ngữ nào, điều đó không đúng. Bạn chắc chắn có thể làm điều này trong Ruby, nơi các phương thức lớp được kế thừa. Đây không phải là trường hợp ai đó không nhận được OO, đây là trường hợp thiếu chức năng trong ngôn ngữ Java. Bạn có thể cố gắng lập luận rằng bạn không bao giờ cần phải kế thừa các phương thức tĩnh (class), nhưng điều đó hoàn toàn sai và tôi sẽ bỏ qua bất kỳ câu trả lời nào tạo ra các điểm như vậy.

+0

Lý do duy nhất một phương pháp tĩnh trừu tượng sẽ làm cho bất kỳ ý nghĩa là vì kế thừa phương thức tĩnh lạ của Java. C# không cho phép bạn kế thừa các phương thức tĩnh, bởi vì thẳng thắn nó không có ý nghĩa (các phương thức tĩnh thuộc về lớp, không phải là cá thể). – Powerlord

+3

@R. Bemrose: Đối với những người trong chúng ta, những người không phải là lập trình viên C#, bạn có thể giải thích cách C# khác với Java về "kế thừa phương thức tĩnh lạ" không? Lần trước tôi đã kiểm tra Java không hỗ trợ kế thừa các phương thức tĩnh. – Asaph

+0

Bạn có thể giải thích tại sao bạn cần chức năng này được thực hiện trong tất cả các lớp dẫn xuất là tĩnh? Tại sao phương pháp trừu tượng bình thường lại không đủ? – Jherico

Trả lời

7

Bạn có một vài lựa chọn:

  1. Sử dụng phản chiếu để xem nếu phương thức tồn tại và sau đó gọi nó.
  2. Tạo chú thích cho phương thức tĩnh có tên là @GetAllWidgetsMethod.

  3. Như những người khác đã nói, cố gắng không sử dụng phương pháp tĩnh.

+2

bạn có thể giải thích cách giải pháp chú thích hoạt động (2) không? – twolfe18

+0

Về cơ bản, bạn có thể xem xét các phương thức tĩnh của lớp và xem phương thức có chú thích và sau đó gọi nó. Tuy nhiên, tùy chọn 1 hoặc 2 cung cấp một cách để thực thi điều đó tại thời gian biên dịch. – Dave

0

Không. Bạn không thể làm điều đó. Nếu bạn sẵn sàng thỏa hiệp và làm cho phương thức không tĩnh hoặc cung cấp một phương thức tĩnh trong lớp trừu tượng của bạn, bạn sẽ có thể viết mã này trong Java.

-2

Nó không có ý nghĩa để làm những gì bạn đang yêu cầu:

Why can't static methods be abstract in Java

+0

làm thế nào để không làm những gì tôi yêu cầu. nếu bạn có một vài lớp mà tất cả đều cần một phương thức tĩnh getAllWidgets (trong đó widget là siêu lớp hoặc giao diện), tại sao nó không hợp lý để yêu cầu một phương thức tĩnh (trong mỗi lớp con) getAllWidgets trả về một tập hợp các cá thể của lớp con/lớp thực hiện giao diện? – twolfe18

+0

-1 vì không đọc câu hỏi của tôi. – twolfe18

1

Có cách nào để làm điều này trong Java?

Tôi không nghĩ rằng có cách để làm điều này bằng bất kỳ ngôn ngữ nào. Không có vấn đề gì với nó, vì các phương thức tĩnh thuộc về một lớp và không thể được gọi là đa hình. Và cho phép các cuộc gọi đa hình là lý do duy nhất cho các giao diện và các lớp trừu tượng tồn tại.

+1

vì vậy trong mỗi lớp con tôi chỉ cần viết một phương thức tĩnh getAllWidgets, không có cách nào để chỉ ra lập trình rằng phương thức đó tồn tại? – twolfe18

+0

Làm thế nào bạn sẽ gọi nó? bên cạnh đó, có lẽ một giải pháp tốt hơn: có phương thức đó được triển khai đầy đủ trong một lớp cơ sở, lấy một đối tượng Lớp làm tham số - hàm tạo lớp cơ sở có thể lấy bánh hoặc đăng ký tất cả các cá thể. –

+1

@Michael những gì bạn mô tả tương tự như những gì tôi đã kết thúc. nhưng đối với câu hỏi đầu tiên của bạn, hãy nói lớp A mở rộng lớp trừu tượng này yêu cầu getBlahBlah tĩnh trừu tượng(), bạn sẽ gọi nó là A.getBlahBlah(). Tôi không thực sự hiểu tại sao mọi người lại coi việc này quá tệ, tôi không nghĩ tôi đang hỏi điều gì đó không hợp lý. – twolfe18

0

Tạo giao diện ngữ cảnh chứa phương pháp của bạn với tên khớp với miền sự cố của bạn. (Đặt tên là "Thế giới" nếu bạn hoàn toàn phải, nhưng phần lớn thời gian có tên tốt hơn)

Vượt qua các trường hợp triển khai của đối tượng ngữ cảnh.

+0

loại xấu xí, nhưng nó có thể hoạt động. tôi có lẽ sẽ không sử dụng điều này mặc dù bởi vì nó xấu hơn so với giải pháp hiện tại của tôi mà là chỉ cần có một phiên bản của phương pháp tĩnh trong mỗi lớp con của tôi. – twolfe18

-1

Ok, có thể câu hỏi của tôi đã được hỏi kém, có vẻ như hầu hết các bạn đã không hiểu những gì tôi đang cố gắng làm. Tuy nhiên, tôi có một giải pháp có phần thỏa đáng.

Trong lớp siêu trừu tượng, tôi sẽ có phương thức tĩnh getAllWidgets (Loại lớp). Trong đó tôi sẽ kiểm tra lớp bạn đã vượt qua nó và thực hiện tìm nạp đúng dựa trên đó. Nói chung tôi muốn tránh đi qua các lớp học và sử dụng các công tắc trên các công cụ như thế này, nhưng tôi sẽ thực hiện một ngoại lệ ở đây.

+2

Đó là một trường hợp rất phổ biến của câu hỏi không được hỏi: bạn đã quyết định một giải pháp không thực hiện được cho vấn đề thực tế của bạn và sau đó hỏi làm thế nào để giải pháp đó hoạt động mà không đề cập đến động lực thực tế của bạn. BTW, bạn không cần phải sử dụng một chuyển đổi ở đây, một bản đồ > nên làm các trick. Thậm chí có thể sử dụng tham số kiểu chung để phương thức trở thành an toàn. –

+1

Bạn không thực sự có được OO (như hầu hết các câu trả lời ở đây đã chỉ ra). Tôi sẽ cung cấp cho bạn một gợi ý, nếu bạn đang dựa vào phương pháp tĩnh cho nhiều hơn các chức năng tầm thường, bạn đang làm sai. Nếu bạn đăng nhiều vấn đề của mình, chúng tôi có thể giúp bạn nhưng cũng như với hầu hết các vấn đề về lập trình, thật khó để giải quyết chính xác mà không có hình ảnh lớn hơn nhiều. Tôi sẽ đề nghị bạn làm lại thiết kế của bạn mà không có statics để phá vỡ thói quen cho người mới bắt đầu. Tôi ước rằng có một cách dễ dàng để chia sẻ các sơ đồ thiết kế kiểu UML - nhìn thấy bạn cũng có thể giúp bạn. –

+0

@Michael - đề xuất của bạn về bản đồ và thông số loại được ghi chú, điểm tốt. Tuy nhiên tôi không có giải pháp này lên kế hoạch khi tôi đặt câu hỏi. – twolfe18

6

Có rất nhiều câu trả lời về 'điều này không có ý nghĩa ..' nhưng thực sự tôi đã gặp một vấn đề tương tự chỉ ngày hôm qua.

Tôi muốn sử dụng thừa kế với các bài kiểm tra đơn vị của mình. Tôi có một API và một số triển khai của nó. Vì vậy, tôi chỉ cần 1 bộ kiểm tra đơn vị cho tất cả các triển khai nhưng với các phương thức thiết lập khác nhau là tĩnh.

Cách giải quyết: tất cả các bài kiểm tra là các lớp trừu tượng, với một số trường tĩnh có công cụ sửa đổi truy cập được bảo vệ. Trong tất cả các triển khai, tôi đã thêm các phương thức tĩnh để thiết lập các trường tĩnh này. Nó hoạt động khá tốt đẹp, và tôi tránh sao chép và dán.

+1

điều này là khá nhiều tương đương với những gì tôi sẽ làm, với một số tinh chỉnh. Tôi cần phải lo lắng về kiểu trả về cho các phương thức tĩnh mặc dù. – twolfe18

0

phương pháp tĩnh không thể trừu tượng được vì chúng không phải là ảo. Vì vậy, bất cứ nơi nào gọi chúng phải có loại cụ thể với việc thực hiện. Nếu bạn muốn thực thi rằng tất cả các triển khai của một giao diện đều có một phương thức tĩnh nhất định, thì điều đó cho thấy một phép thử đơn vị là bắt buộc.

abstract class A 
{ 
    public static void foo() 
    { 
     java.lang.System.out.println("A::foo"); 
    } 

    public void bar() 
    { 

     java.lang.System.out.println("A::bar"); 
    } 
} 

class B extends A 
{ 
    public static void foo() 
    { 
     java.lang.System.out.println("B::foo"); 
    } 

    public void bar() 
    { 

     java.lang.System.out.println("B::bar"); 
    } 
} 

public class Main 
{ 
    public static void main(String[] args) 
    { 
     B b = new B(); 
     b.foo(); 
     b.bar(); 

     A a = b; 
     a.foo(); 
     a.bar(); 
    } 
} 
1

Hệ thống kiểu cho phép bạn thể hiện một số ràng buộc giữa các loại nhưng giới hạn. Đó là lý do tại sao javadocs được rải rác với những ràng buộc trong ngôn ngữ của con người, yêu cầu mọi người tuân theo các quy tắc mà trình biên dịch không thể kiểm tra.

nếu bạn muốn mở rộng vượt quá ngôn ngữ cung cấp nguyên bản, bạn có thể viết công cụ phân tích tĩnh của riêng mình. đó không phải là không phổ biến. ví dụ: findbug. IDE cũng làm điều đó quá, họ kiểm tra điều vượt ra ngoài những gì ngôn ngữ ra lệnh. bạn có thể viết một plug-in để thực thi rằng một lớp con phải có một phương thức tĩnh của chữ ký đó.

trong trường hợp của bạn, không đáng. có javadoc trong các trình triển khai yêu cầu siêu lớp bao gồm một phương thức tĩnh, đủ tốt.

Tôi sẽ cung cấp một cách phức tạp để thể hiện ràng buộc của bạn, tuy nhiên, KHÔNG LÀM VIỆC. mọi người nhận được thực sự mang đi làm cho tất cả mọi thứ có thể kiểm tra tại thời gian biên dịch, ở mức giá làm cho mã không đọc được.

interface WidgetEnumerator 
{ 
    List getAllWidgets(); 
} 

public class Abs<T extends WidgetEnumerator> 
{ 
    static List getAllWidgets(Class<? extends Abs> clazz){ ... } 
} 

public class Sub extends Abs<SubWidgetEnumerator> 
{ 
} 

public class SubWidgetEnumerator implements WidgetEnumerator 
{ 
    public List getAllWidgets() { ... } 
} 

Cách hoạt động: đối với mọi phân lớp Abs, buộc phải cung cấp triển khai WidgetEnumerator. tác giả phân lớp không thể quên điều đó. Bây giờ, hãy gọi Abs.getAllWidgets(Sub.class) chứa đầy đủ thông tin để giải quyết việc triển khai đó, tức là SubWidgetEnumerator. Nó được thực hiện thông qua sự phản chiếu, nhưng nó là loại an toàn, không có các chuỗi ký tự liên quan đến chuỗi.

-1

Tôi muốn tạo lớp WidgetCollection với lớp bên trong Widget trừu tượng.

Bạn có thể mở rộng lớp WidgetCollection.Widget cho từng loại Widget của bạn.

Không có phương pháp tĩnh cần thiết.

Ví dụ (không được biên dịch hoặc kiểm tra):

class WidgetCollection<W extends Widget> { 
    Set<W> widgets = new HashSet<W>(); 

    Set<W> getAll() { 
     return widgets; 
    } 

    abstract class Widget { 

     Widget() { 
      widgets.add(this); 
     } 

     abstract String getName(); 
    } 

    public static void main(String[] args) { 
     WidgetCollection<AWidget> aWidgets = new WidgetCollection<AWidget>(); 
     a.new AWidget(); 

     Set<AWidget> widgets = aWidgets.getAll(); 
    } 
} 

class AWidget extends Widget { 
    String getName() { 
     return "AWidget"; 
    } 
} 
1

Tôi nghĩ rằng tôi có thể cung cấp cho bạn một câu trả lời tốt hơn sau khi nhìn thấy sửa đổi của bạn - đặt cược tốt nhất của bạn có lẽ là một mô hình nhà máy. (Không đáng yêu, nhưng tốt hơn singleton).

abstract class Widget 
    public static Widget[] getAllWidgetsOfType(Class widgetType) { 
     if(widgetType instanceof ...) 
    } 
class Ball extends Widget 
class Stick extends Widget 
class Toy extends Widget 

Đây không phải là cách hay để làm điều đó, nhưng nó là điển hình. Hibernate là công cụ bạn thường sử dụng để giải quyết vấn đề này, đây chính xác là những gì nó được thiết kế cho.

Vấn đề lớn là nó đòi hỏi phải chỉnh sửa lớp cơ sở bất cứ khi nào bạn thêm một lớp mới của một loại nhất định. Điều này không thể được xung quanh mà không phản ánh. Nếu bạn muốn sử dụng phản chiếu, sau đó bạn có thể thực hiện nó theo cách này (psuedocode, tôi sẽ không phải tìm kiếm cú pháp chính xác cho sự phản ánh, nhưng nó không phải là phức tạp hơn nhiều so với điều này):

public static Widget[] getAllWidgetsOfType(Class widgetType) { 
    Method staticMethod=widgetType.getStaticMethod("getAllInstances"); 
    return staticMethod.invoke(); 
} 

này sẽ cung cấp cho các giải pháp mà bạn đã yêu cầu (bị làm phiền bởi sự cần thiết phải sửa đổi các lớp cơ sở mỗi khi bạn thêm một lớp con là một bản năng tốt).

Bạn cũng có thể biến nó thành phương thức thể hiện thay vì tĩnh. Nó không cần thiết, nhưng sau đó bạn có thể thử nghiệm phương thức (abstract) trong Widget.

Một lần nữa, tất cả điều này là không cần thiết và cẩu thả so với Hibernate ...

Edit: Nếu bạn thông qua trong một live dụ "Empty" của một quả bóng, gậy hoặc đồ chơi thay vì đó là "Class" đối tượng, bạn sau đó có thể chỉ cần gọi một phương pháp kế thừa và không sử dụng sự phản chiếu nào cả. Điều này cũng sẽ làm việc nhưng bạn phải mở rộng định nghĩa của một Widget để bao gồm một thể hiện "rỗng" được sử dụng như một khóa.

2

Tĩnh phương pháp có liên quan đến toàn bộ lớp đối tượng, không phải từng trường hợp riêng lẻ. Cho phép một phương thức tĩnh được ghi đè phá vỡ mệnh lệnh này.

Điều đầu tiên tôi sẽ xem xét là truy cập cơ sở dữ liệu của bạn từ ngữ cảnh không tĩnh. Đây thực sự là tiêu chuẩn cho các ứng dụng Java.

Nếu bạn hoàn toàn phải sử dụng một phương pháp tĩnh, sau đó nó được tham số hóa với các đối số cụ thể cụ thể (của một loại chung) để cho phép các lớp con khác tương tác với nó. Sau đó, hãy gọi đơn lẻ phương pháp tĩnh này từ các phương pháp đa hình của bạn.

4

Tôi cũng đang xử lý vấn đề này. Đối với những người khăng khăng rằng nó "không có ý nghĩa", tôi sẽ mời bạn suy nghĩ bên ngoài hộp ngữ nghĩa đó trong một khoảnh khắc. Chương trình tôi đang làm việc với vốn dĩ là về sự phản chiếu.

Phản ánh, như bạn biết, có thể mất ba đơn đặt hàng lớn hơn độ dài của hàm gọi nhị phân thẳng lên. Đó là một vấn đề không thể tránh khỏi và phần mềm cần phải chuyển đến càng nhiều máy càng tốt, một số máy sẽ chậm hơn 32 bit so với máy phát triển của tôi. Do đó, khả năng ứng dụng của một lớp vào hoạt động được yêu cầu cần phải được kiểm tra thông qua một phương thức tĩnh và tất cả các phương thức phản chiếu được chạy cùng một lúc trong quá trình khởi động mô-đun.

Mọi thứ hoạt động, trước tiên và quan trọng nhất. Tôi đã xây dựng toàn bộ điều. Sự bắt giữ duy nhất là một mô-đun có thể được biên dịch trong một lớp. Mà không cần kiểm tra thời gian biên dịch để xem liệu hàm tĩnh xác định có tồn tại hay không, dẫn đến một lớp vô dụng vô dụng. Không có số nhận dạng và thông tin được bao gồm của nó, vì lý do bảo mật, mô-đun sẽ không được tải.

Tôi hiểu rõ vấn đề với định nghĩa hoàn chỉnh về "trừu tượng" và "tĩnh" và hiểu rằng chúng không hợp lý với nhau. Tuy nhiên, khả năng có một phương thức lớp được trình biên dịch thực thi để đưa vào là thiếu Java, và nhiều như tôi thích ngôn ngữ, tôi nhớ nó.Vì vậy, đây là một hạn chế của con người đối với mọi lập trình viên từng làm việc trên phần mềm, mà tôi chắc chắn rằng tất cả chúng ta đều có thể đồng ý là một nỗi đau.

+1

điều này không trả lời câu hỏi và giống như một nhận xét lang thang – oers

0

Đối với những gì tôi đáng được biết chính xác những gì bạn đang cố gắng làm.

Tôi tìm thấy bài viết này trong khi tìm kiếm lý do tôi cũng không thể làm được.

Trong trường hợp của tôi, tôi có hàng trăm lớp kế thừa từ một cơ sở căn cứ trung tâm và tôi muốn chỉ đơn giản là để có được một tài liệu tham khảo như thế này:

ValueImSearchingFor visf = StaticClass.someArbitraryValue()

Tôi không muốn để viết/duy trì someArbitraryValue() cho mỗi một trong hàng trăm lớp thừa kế - tôi chỉ muốn viết logic một lần và có nó là một giá trị Class-Sepcific duy nhất cho mỗi và mọi lớp viết trong tương lai KHÔNG chạm vào lớp cơ sở.

Có Tôi hoàn toàn nhận được OO - Tôi đã viết Java miễn là nó đã có sẵn.

Các lớp cụ thể này giống như "Định nghĩa" trái ngược với đối tượng thực tế và tôi không muốn khởi tạo một lần mỗi khi tôi chỉ cần xem những gì một sốArbitraryValue() thực sự là.

Hãy suy nghĩ về nó như là một CUỐI CÙNG TÌNH TRẠNG CÔNG CỘNG cho phép bạn chạy một phương pháp ONCE để thiết lập nó ban đầu. (Kinda như bạn có thể làm khi bạn xác định một Enum thực sự ...)