修复 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,我假设 configConnectorConfig 的一个实例。如果这是正确的,那么不幸的是 getTransport() returns Class 而不是 Class<?>Class<? extends Transport>。这是您正在使用的库的 API 的问题;如果问题尚不存在,您可能需要考虑提交问题。

未检查对 getDeclaredConstructor 的调用,因为它 returns Constructor<T>——其中 T 来自 Class 的通用参数——但你有一个原始 Class。您有两个选项可以消除警告:

  1. Class 对象转换为 Class<?>

    Transport t = (Transport) ((Class<?>) config.getTransport()).getDeclaredConstructor().newInstance();
    
  2. 在尽可能小的范围内使用@SuppressWarnings("unchecked")

    @SuppressWarnings("unchecked")
    Transport t = (Transport) config.getTransport().newInstance();
    

使用 Class#newInstance 没有导致此 "unchecked call" 警告的原因是因为该方法 returns TClass 是原始方法时,解析为 Object—non-generic 类型。

感谢您的宝贵建议

我已按照以下方式应用这些建议,以使其更易于阅读

        Class<?> transClass = config.getTransport();
        Transport t = (Transport) transClass.getDeclaredConstructor().newInstance();
        t.setConfig(this);

在遵循 Javadoc 中的建议替换时,还需要考虑两个方面:

clz.newInstance()

与:

clz.getDeclaredConstructor().newInstance()

首先,Class#getDeclaredConstructor 可能会抛出 InvocationTargetExceptionNoSuchMethodExceptionReflectiveOperationException 的两种形式),而 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;
    }
}