修复 Java newInstance() 已弃用
Fix to Java newInstance() deprecated
Java 来自 https://github.com/forcedotcom/wsc 的代码包含一些用于创建新实例的弃用代码
传输是一个接口
public interface Transport {
}
......
Transport t = (Transport) config.getTransport().newInstance();
t.setConfig(config);
return t
我尝试使用
修复的方法
Transport t = (Transport) config.getTransport().getDeclaredConstructor().newInstance();
t.setConfig(config);
return t
这会创建一个警告“未经检查调用 getDeclaredConstructor(Class..) 作为原始类型的成员 'java.lang.Class' '
我正在寻找一种更好的方法来修复这个已弃用的调用。
这段代码不是我写的。它提供 Java 到 Salesforce.com 的 SOAP 连接。我已经编写了自己的代码以将其与 Java 8 一起使用,但是,我认为更新代码以与 Java 9+
一起使用会很有用
如 Class#newInstance()
的弃用消息所述,正确的替代方法是使用:
其次是:
没有"better way"因为你有的已经是正解了。您遇到的问题与 raw types 有关。查看您提供的 GitHub link,我假设 config
是 ConnectorConfig
的一个实例。如果这是正确的,那么不幸的是 getTransport()
returns Class
而不是 Class<?>
或 Class<? extends Transport>
。这是您正在使用的库的 API 的问题;如果问题尚不存在,您可能需要考虑提交问题。
未检查对 getDeclaredConstructor
的调用,因为它 returns Constructor<T>
——其中 T
来自 Class
的通用参数——但你有一个原始 Class
。您有两个选项可以消除警告:
将 Class
对象转换为 Class<?>
。
Transport t = (Transport) ((Class<?>) config.getTransport()).getDeclaredConstructor().newInstance();
在尽可能小的范围内使用@SuppressWarnings("unchecked")
。
@SuppressWarnings("unchecked")
Transport t = (Transport) config.getTransport().newInstance();
使用 Class#newInstance
没有导致此 "unchecked call" 警告的原因是因为该方法 returns T
当 Class
是原始方法时,解析为 Object
—non-generic 类型。
感谢您的宝贵建议
我已按照以下方式应用这些建议,以使其更易于阅读
Class<?> transClass = config.getTransport();
Transport t = (Transport) transClass.getDeclaredConstructor().newInstance();
t.setConfig(this);
在遵循 Javadoc 中的建议替换时,还需要考虑两个方面:
clz.newInstance()
与:
clz.getDeclaredConstructor().newInstance()
首先,Class#getDeclaredConstructor
可能会抛出 InvocationTargetException
或 NoSuchMethodException
(ReflectiveOperationException
的两种形式),而 Class#newInstance
会抛出 InstantiationException
这些条件。
因为这些不是 RuntimeException
的类型,它们需要被显式处理,可以通过捕获它们并将它们设置为可以抛出的(新)InstantiationException
的原因,以保留调用代码的签名。
其次,Class#getDeclaredConstructor
会导致进行额外的 "accessDeclaredMembers"
安全检查(以及 Class#newInstance
也进行的 checkPackageAccess()
检查)。
因此,可能需要采取额外的步骤(例如使用 AccessController#doPrivileged
)以确保调用者不会通过此额外检查。
所以,像这样的方法:
Object createInstance(Class clz)
throws InstantiationException, IllegalAccessException {
return clz.newInstance();
}
可能,一旦正确修改,看起来更像这样:
Object createInstance(Class<?> clz)
throws InstantiationException, IllegalAccessException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws InstantiationException, IllegalAccessException {
try {
return clz.getDeclaredConstructor().newInstance();
} catch (InvocationTargetException|NoSuchMethodException e) {
throw (InstantiationException)((new InstantiationException()).initCause(e));
}
}
});
} catch (PrivilegedActionException pae) {
Exception e = pae.getException();
if (e instanceof InstantiationException) throw (InstantiationException)e;
throw (IllegalAccessException)e;
}
}
Java 来自 https://github.com/forcedotcom/wsc 的代码包含一些用于创建新实例的弃用代码
传输是一个接口
public interface Transport {
}
......
Transport t = (Transport) config.getTransport().newInstance();
t.setConfig(config);
return t
我尝试使用
修复的方法 Transport t = (Transport) config.getTransport().getDeclaredConstructor().newInstance();
t.setConfig(config);
return t
这会创建一个警告“未经检查调用 getDeclaredConstructor(Class..) 作为原始类型的成员 'java.lang.Class' '
我正在寻找一种更好的方法来修复这个已弃用的调用。
这段代码不是我写的。它提供 Java 到 Salesforce.com 的 SOAP 连接。我已经编写了自己的代码以将其与 Java 8 一起使用,但是,我认为更新代码以与 Java 9+
一起使用会很有用如 Class#newInstance()
的弃用消息所述,正确的替代方法是使用:
其次是:
没有"better way"因为你有的已经是正解了。您遇到的问题与 raw types 有关。查看您提供的 GitHub link,我假设 config
是 ConnectorConfig
的一个实例。如果这是正确的,那么不幸的是 getTransport()
returns Class
而不是 Class<?>
或 Class<? extends Transport>
。这是您正在使用的库的 API 的问题;如果问题尚不存在,您可能需要考虑提交问题。
未检查对 getDeclaredConstructor
的调用,因为它 returns Constructor<T>
——其中 T
来自 Class
的通用参数——但你有一个原始 Class
。您有两个选项可以消除警告:
将
Class
对象转换为Class<?>
。Transport t = (Transport) ((Class<?>) config.getTransport()).getDeclaredConstructor().newInstance();
在尽可能小的范围内使用
@SuppressWarnings("unchecked")
。@SuppressWarnings("unchecked") Transport t = (Transport) config.getTransport().newInstance();
使用 Class#newInstance
没有导致此 "unchecked call" 警告的原因是因为该方法 returns T
当 Class
是原始方法时,解析为 Object
—non-generic 类型。
感谢您的宝贵建议
我已按照以下方式应用这些建议,以使其更易于阅读
Class<?> transClass = config.getTransport();
Transport t = (Transport) transClass.getDeclaredConstructor().newInstance();
t.setConfig(this);
在遵循 Javadoc 中的建议替换时,还需要考虑两个方面:
clz.newInstance()
与:
clz.getDeclaredConstructor().newInstance()
首先,Class#getDeclaredConstructor
可能会抛出 InvocationTargetException
或 NoSuchMethodException
(ReflectiveOperationException
的两种形式),而 Class#newInstance
会抛出 InstantiationException
这些条件。
因为这些不是 RuntimeException
的类型,它们需要被显式处理,可以通过捕获它们并将它们设置为可以抛出的(新)InstantiationException
的原因,以保留调用代码的签名。
其次,Class#getDeclaredConstructor
会导致进行额外的 "accessDeclaredMembers"
安全检查(以及 Class#newInstance
也进行的 checkPackageAccess()
检查)。
因此,可能需要采取额外的步骤(例如使用 AccessController#doPrivileged
)以确保调用者不会通过此额外检查。
所以,像这样的方法:
Object createInstance(Class clz)
throws InstantiationException, IllegalAccessException {
return clz.newInstance();
}
可能,一旦正确修改,看起来更像这样:
Object createInstance(Class<?> clz)
throws InstantiationException, IllegalAccessException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws InstantiationException, IllegalAccessException {
try {
return clz.getDeclaredConstructor().newInstance();
} catch (InvocationTargetException|NoSuchMethodException e) {
throw (InstantiationException)((new InstantiationException()).initCause(e));
}
}
});
} catch (PrivilegedActionException pae) {
Exception e = pae.getException();
if (e instanceof InstantiationException) throw (InstantiationException)e;
throw (IllegalAccessException)e;
}
}