我有一些丑陋的代码,想要重构它:
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。我有一个约束,即构造函数不会抛出 - 否则这一切都很容易。
答案 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
实例化的复杂性。socket
和address
都设置为实例,或者设置为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;
}
}
...