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

fad*_*bee 13 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;
    }
    ...
Run Code Online (Sandbox Code Playgroud)

导致丑陋的问题是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;
    }
    ...
Run Code Online (Sandbox Code Playgroud)

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

有什么建议?

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

h22*_*h22 13

让构造函数抛出异常.离开final未分配是确定的,如果在构造函数不正常终止,在此情况下,不返回任何对象.

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


Lyu*_*riv 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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

上述解决方案可能包含以下注意事项:

  • 它只有一个构造函数,只将参数分配给字段.因此,您可以轻松拥有final字段.这非常类似于我记得在C#和Scala中称为主要构造函数的东西.
  • 静态工厂方法隐藏了UdpTransport实例化的复杂性.
  • 为简单起见,静态工厂方法返回一个实例,其中包含both 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
    }
Run Code Online (Sandbox Code Playgroud)
  • 最后,恕我直言,用这种方法打印堆栈跟踪并不是一个好主意.