2009-07-06 47 views
5

Phương thức ObjectOutputStream.writeStreamHeader() có thể được ghi đè để thêm hoặc thêm dữ liệu vào tiêu đề. Tuy nhiên, nếu dữ liệu được dựa trên một cuộc tranh cãi được truyền cho constructor lớp được thừa kế của như:Làm thế nào để ghi đè lên ObjectOutputStream.writeStreamHeader()?

public class MyObjectOutputStream extends ObjectOutputStream { 

    public MyObjectOutputStream(int myData, OutputStream out) throws IOException { 
     super(out); 
     m_myData = myData; 
    } 

    protected void writeStreamHeader() throws IOException { 
     write(m_myData);   // WRONG: m_myData not initialized yet 
     super.writeStreamHeader(); 
    } 

    private final int m_myData; 
} 

nó không hoạt động vì super() được gọi trước khi m_myData được khởi tạo và super() cuộc gọi writeStreamHeader(). Cách duy nhất tôi có thể nghĩ đến việc xung quanh này là bằng cách sử dụng ThreadLocal như:

public class MyObjectOutputStream extends ObjectOutputStream { 

    public MyObjectOutputStream(int myData, OutputStream out) throws IOException { 
     super(thunk(myData, out)); 
    } 

    protected void writeStreamHeader() throws IOException { 
     write(m_myData.get().intValue()); 
     super.writeStreamHeader(); 
    } 

    private static OutputStream thunk(int myData, OutputStream out) { 
     m_myData.set(myData); 
     return out; 
    } 

    private static final ThreadLocal<Integer> m_myData = new ThreadLocal<Integer>(); 
} 

Điều này dường như làm việc, nhưng là có một (ít phiền phức) cách tốt hơn?

Trả lời

3

Có một cách chung để giải quyết vấn đề này. Tạo lớp và lớp bên trong và tham chiếu biến trong phạm vi bên ngoài.(Lưu ý, điều này chỉ làm việc với -target 1.4 hoặc greter, đó là mặc định trong các phiên bản hiện tại của javac. Với -target 1.3 bạn sẽ nhận được một NPE.)

public static ObjectOutputStream newInstance(
    final int myData, final OutputStream out 
) throws IOException { 
    return new ObjectOutputStream(out) { 
     @Override 
     protected void writeStreamHeader() throws IOException { 
      write(myData); 
      super.writeStreamHeader(); 
     } 
    }; 
} 

Tuy nhiên, nó có thể là dễ dàng hơn chỉ để viết dữ liệu ra trước khi xây dựng số ObjectOuputStream.

+0

Tôi thích giải pháp này vì nó ghi đè writeStreamHeader() "như dự định" và tiêu đề được viết vào "thời điểm thích hợp" và không đưa ra giả định về khi phương thức được gọi bởi hàm tạo lớp cơ sở. Như vậy, đó là "bằng chứng trong tương lai". –

+0

Gọn gàng. Rõ ràng phạm vi bên ngoài được khởi tạo trước hàm tạo của lớp bậc trên. – Thilo

+0

Vâng, thông số VM phải được thay đổi để cho phép điều đó. Nó vẫn có thể trở nên phức tạp - lần cuối tôi kiểm tra một ví dụ trong JLS đã không làm việc với javac của thời gian. –

0

Sử dụng bố cục thay vì thừa kế.

public class MyObjOutStream implements DataOutput, ObjectOutput, ObjectStreamConstants { 
    //same interfaces that ObjectOutputStream implements 

    private ObjectOutputStream objOutStream; 

    //implement all the methods below 
} 

Instance ObjectOutputStream chỉ khi bạn đã sẵn sàng để làm như vậy. Các phương pháp còn lại bạn cần thực hiện trong giao diện có thể chỉ cần gọi cùng một phương thức trên objOutStream

+0

điều này thậm chí còn khó khăn hơn bằng cách sử dụng ThreadLocal imho – dfa

+0

Làm thế nào là điều đó clunky? Thành phần rất tao nhã. Vấn đề duy nhất là tất cả các mã boilerplate (các phương thức của phương thức) vì Java thiếu một cú pháp tốt đẹp cho các đại biểu. Nhưng Eclipse có thể tự động tạo ra chúng. – Thilo

+2

@ Thilo, tôi thích giải pháp của bạn hơn bằng cách sử dụng thành phần luồng – dfa

1

Thường là một ý tưởng tồi khi gọi phương thức không phải cuối cùng từ hàm tạo (để biết chính xác lý do bạn đã trình bày).

Bạn có thể đạt được chuỗi tuần tự tùy chỉnh của mình mà không cần mở rộng ObjectOutputStream không? Tôi đang nghĩ về thành phần luồng. Ví dụ, bạn có thể thêm vào đầu trang của bạn bằng cách viết nó vào OutputStream bên dưới trước khi ObjectOutputStream làm. Điều này rõ ràng không thể được thực hiện trong một lớp con của ObjectOutputStream, nhưng nó có thể dễ dàng được thực hiện từ bên ngoài.

out.write(myExtraHeader); 
    ObjectOutputStream oos = new ObjectOutputStream(out); 

Nếu bạn muốn, bạn có thể bọc này tất cả lên độc đáo đằng sau giao diện ObjectOutput như Stu Thompson gợi ý trong câu trả lời của mình, để nó có thể nhìn ra bên ngoài gần giống như một ObjectOutputStream.

Cập nhật:

Nhìn vào JavaDocs và nguồn cho ObjectOutputStream, có một thứ hai (được bảo vệ) constructor, điều đó không gọi writeStreamHeader().

Tuy nhiên, hàm tạo này cũng không khởi tạo các cấu trúc bên trong khác. Để báo các tài liệu, nó được dự định "cho các lớp con hoàn toàn reimplementing ObjectOutputStream để không phải phân bổ dữ liệu cá nhân chỉ được sử dụng bởi việc thực hiện này của ObjectOutputStream". Trong trường hợp này, nó cũng gọi một số phương thức khác, chẳng hạn như "writeObjectOverride". Lộn xộn ...

1

Bạn không thể làm điều đó như thế này. Bỏ qua cuộc gọi writeStreamHeader từ các nhà xây dựng siêu và thực hiện một mình, khi bạn đã khởi tạo các lĩnh vực cần thiết:

public class MyObjectOutputStream extends ObjectOutputStream { 

private boolean initalized = false; 
private final int m_myData; 

protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException { 
    super(out); 
    m_myData = myData; 
    initalized = true; 
    writeStreamHeader(); 
} 

protected void writeStreamHeader() throws IOException { 

    if(!initalized){ 
     return; 
    } 

    write(m_myData); 
    super.writeStreamHeader(); 
} 
} 

EDIT:

Hoặc, theo đề nghị của Thilo, nó có thể được viết như sau:

public class MyObjectOutputStream extends ObjectOutputStream { 

    private final int m_myData; 

    protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException { 
     super(out); 
     m_myData = myData; 
     write(m_myData); 
     super.writeStreamHeader(); 
    } 

    protected void writeStreamHeader() throws IOException { 
     // work is done in the constructor 
    } 
} 
+0

Điều đó có thể hoạt động, tôi giả sử ... – Thilo

+1

Dọc theo cùng một dòng, có thể rõ ràng hơn/sạch hơn: Ghi đè lên writeStreamHeader để không làm gì và gọi super.writeStreamHeader() từ nhà xây dựng của bạn. Bằng cách này, không cần cờ init. – Thilo

+0

Ý tưởng tuyệt vời, chỉ cần thêm một ví dụ. –

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