簡體   English   中英

使用最終成員處理構造函數中捕獲的Java異常

[英]Handling Java exceptions caught in constructors, with final members

我有一些丑陋的代碼,想重構它:

public class UdpTransport extends AbstractLayer<byte[]> {
    private final DatagramSocket socket;
    private final InetAddress address;
    private final int port;
    /* boolean dead is provided by superclass */

    public UdpTransport(String host, int port) {
        this.port = port;
        InetAddress tmp_address = null;
        try {
            tmp_address = InetAddress.getByName(host);
        } catch (UnknownHostException e) {
            e.printStackTrace();
            dead = true;
            socket = null;
            address = null;
            return;
        }
        address = tmp_address;
        DatagramSocket tmp_socket = null;
        try {
            tmp_socket = new DatagramSocket();
        } catch (SocketException e) {
            e.printStackTrace();
            dead = true;
            socket = null;
            return;
        }
        socket = tmp_socket;
    }
    ...

導致丑陋的問題是final成員與捕獲的異常之間的交互。 如果可能的話,我想讓成員保持final

我想按以下方式編寫代碼,但是Java編譯器無法分析控制流-無法對address進行第二次分配,因為必須進行第一次嘗試分配才能使控制到達catch子句。

public UdpTransport(String host, int port) {
    this.port = port;
    try {
        address = InetAddress.getByName(host);
    } catch (UnknownHostException e) {
        e.printStackTrace();
        dead = true;
        address = null; // can only have reached here if exception was thrown 
        socket = null;
        return;
    }
    ...

Error:(27, 13) error: variable address might already have been assigned

有什么建議嗎?

PS我有一個約束,那就是構造函數不要拋出-否則這一切都將很容易。

讓構造函數拋出異常。 如果構造函數未正常終止,則將final保留為undefined是可以的,因為在這種情況下,不會返回任何對象。

代碼中最丑陋的部分是在構造函數中捕獲異常,然后返回現有的但無效的實例。

如果您可以自由使用私有構造函數,則可以將構造函數隱藏在公共靜態工廠方法后面,該方法可以返回UdpTransport類的不同實例。 比方說:

public final class UdpTransport
        extends AbstractLayer<byte[]> {

    private final DatagramSocket socket;
    private final InetAddress address;
    private final int port;
    /* boolean dead is provided by superclass */

    private UdpTransport(final boolean dead, final DatagramSocket socket, final InetAddress address, final int port) {
        super(dead);
        this.socket = socket;
        this.address = address;
        this.port = port;
    }

    public static UdpTransport createUdpTransport(final String host, final int port) {
        try {
            return new UdpTransport(false, new DatagramSocket(), getByName(host), port);
        } catch ( final SocketException | UnknownHostException ex ) {
            ex.printStackTrace();
            return new UdpTransport(true, null, null, port);
        }
    }

}

上面的解決方案可能有以下注意事項:

  • 它只有一個構造函數,僅將參數分配給字段。 因此,您可以輕松擁有final字段。 這與我記得在C#中可能稱為Scala的主要構造函數非常相似。
  • 靜態工廠方法隱藏了UdpTransport實例化的復雜性。
  • 為簡單起見,靜態工廠方法返回一個實例,該實例的socketaddress設置為真實實例,或者將它們設置為null表示無效狀態。 因此,此實例狀態可能與問題中的代碼所產生的狀態略有不同。
  • 這種工廠方法允許您隱藏返回的實例的實際實現,因此以下聲明是完全有效的: public static AbstractLayer<byte[]> createUdpTransport(final String host, final int port) (請注意返回類型)。 這種方法的強大之處在於,如果可能的話,您可以根據需要用任何子類替換返回的值,除非您使用特定於UdpTransport公共接口。
  • 同樣,如果您對無效的狀態對象沒問題,那么我想,無效的狀態實例不應該包含允許您進行以下操作的真實端口值(假設-1可以表示無效的端口值,或者如果您認為無效,則可以為Integer )可以自由更改類的字段,並且原始包裝器不受您的限制):
private static final AbstractLayer<byte[]> deadUdpTransport = new UdpTransport(true, null, null, -1);
...
public static AbstractLayer<byte[]> createUdpTransport(final String host, final int port) {
    try {
        return new UdpTransport(false, new DatagramSocket(), getByName(host), port);
    } catch ( final SocketException | UnknownHostException ex ) {
        ex.printStackTrace();
        return deadUdpTransport; // it's safe unless UdpTransport is immutable
    }
  • 最后,恕我直言,以這種方法打印堆棧跟蹤不是一個好主意。

構造函數的兩個版本,您的屬性保持最終狀態。 請注意,我不認為您的原始方法通常是“丑陋的”。 但是,由於try-catch-block對於兩個異常都是相同的,因此可以進行改進。 這成為我的構造函數#1。

public UdpTransport(String host, int port) {
    InetAddress byName;
    DatagramSocket datagramSocket;

    try {
        byName = InetAddress.getByName(host);
        datagramSocket = new DatagramSocket(); 
    } catch (UnknownHostException | SocketException e) {
        e.printStackTrace();
        dead = true;
        datagramSocket = null;
        byName = null;
    }
    this.port = port;
    this.socket = datagramSocket;
    this.address = byName;
}

public UdpTransport(int port, String host) {

    this.port = port;
    this.socket = createSocket();
    this.address = findAddress(host);
}

private DatagramSocket createSocket() {
    DatagramSocket result;
    try {
        result = new DatagramSocket(); 
    } catch (SocketException e) {
        e.printStackTrace();
        this.dead = true;
        result = null;
    }
    return result;
}

private InetAddress findAddress(String host) {
    InetAddress result;
    try {
        result = InetAddress.getByName(host);
    } catch (UnknownHostException e) {
        e.printStackTrace();
        this.dead = true;
        result = null;
    }
    return result;
}

此構造函數根本沒有理由捕獲異常。 充滿null值的對象對應用程序毫無用處。 構造函數應該拋出該異常。 沒有抓住它。

像這樣的事情呢:

public class UdpTransport extends AbstractLayer<byte[]> {
    private final DatagramSocket socket;
    private final InetAddress address;
    private final int port;

    public static UdpTransport create(String host, int port) {
        InetAddress address = null;
        DatagramSocket socket = null;
        try {
            address = InetAddress.getByName(host);
            socket = new DatagramSocket();
        } catch (UnknownHostException | SocketException e) {
            e.printStackTrace();
        }
        return new UdpTransport(socket, address, port);
    }

    private UdpTransport(DatagramSocket socket, InetAddress address, int port) {
        this.port = port;
        this.address = address;
        this.socket = socket;
        if(address == null || socket == null) {
           dead = true;
        }
    }
    ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM