处理构造函数中捕获的Java异常,以及最终成员

时间:2015-08-18 09:42:51

标签: java constructor checked-exceptions

我有一些丑陋的代码,想要重构它:

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

有什么建议吗?

P.S。我有一个约束,即构造函数不会抛出 - 否则这一切都很容易。

5 个答案:

答案 0 :(得分:13)

让构造函数抛出异常。如果构造函数没有正常终止,则保留final未分配是正常的,因为在这种情况下不会返回任何对象。

代码中最丑陋的部分是在构造函数中捕获异常,然后返回现有但无效的实例。

答案 1 :(得分:8)

如果您可以自由使用私有构造函数,则可以将构造函数隐藏在公共静态工厂方法后面,该方法可以返回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
    }
  • 最后,恕我直言,用这种方法打印堆栈跟踪并不是一个好主意。

答案 2 :(得分:4)

构造函数的两个版本,其中属性保持最终。请注意,我不认为您的原始方法通常是“丑陋的”。但是它可以改进,因为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;
}

答案 3 :(得分:2)

这个构造函数没有正当理由来捕获异常。一个充满null值的对象对应用程序没有任何用处。构造函数应抛出该异常。没有抓住它。

答案 4 :(得分:0)

这样的事情:

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;
        }
    }
    ...