修复不推荐使用的Java newInstance()

时间:2019-05-10 13:50:38

标签: java deprecated

https://github.com/forcedotcom/wsc中的Java代码包含一些不推荐使用的代码来创建新实例

运输是一个界面

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

这将创建一个警告“未选中作为原始类型'java.lang.Class'的成员的getDeclaredConstructor(Class ..)调用''

我正在寻找一种更好的方法来解决此已弃用的呼叫。

此代码不是我编写的。它提供了与Salesforce.com的Java SOAP连接。我已经编写了自己的代码以在Java 8中使用它,但是,我认为更新代码以与Java 9+一起使用将很有用。

3 个答案:

答案 0 :(得分:2)

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

clz.newInstance()

与:

clz.getDeclaredConstructor().newInstance()

首先,Class#getDeclaredConstructor 可能会抛出 InvocationTargetExceptionNoSuchMethodExceptionReflectiveOperationException 的两种形式),而对于这些条件,Class#newInstance 会抛出 InstantiationException .

由于这些不是 RuntimeException 的类型,因此需要明确处理它们,可以说是通过捕获它们并将它们设置为可以抛出的(新)InstantiationException 的原因,以保留调用代码的签名。

其次,Class#getDeclaredConstructor 会导致进行额外的 "accessDeclaredMembers" 安全检查(以及 checkPackageAccess() 也会进行的 Class#newInstance 检查)。

因此,可能需要采取额外的步骤(例如使用 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;
    }
}

答案 1 :(得分:0)

Class#newInstance()弃用消息所述,正确的替代方法是使用:

其次:

没有“更好的方法”,因为您所拥有的已经是正确的解决方案。您遇到的问题与raw types有关。查看您提供的GitHub链接,我假设configConnectorConfig的实例。如果正确,那么不幸的是,getTransport()返回Class而不是Class<?>Class<? extends Transport>。这是您正在使用的库的API的问题;您可能需要考虑提交一个尚不存在的问题。

未选中对getDeclaredConstructor的调用,因为它返回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不会导致此“未检查的呼叫”警告的原因是因为该方法返回了T,当Class原始时,它会简单地解析为Object —非通用类型。

答案 2 :(得分:0)

感谢出色的建议

我以以下方式应用了建议,以使其更容易阅读

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