2

Java code from https://github.com/forcedotcom/wsc contains some deprecated code to create new instances

Transport is an interface

public interface Transport {

}

......

        Transport t = (Transport) config.getTransport().newInstance();
        t.setConfig(config);
        return t 

methods that I have tried to fix by using

        Transport t = (Transport) config.getTransport().getDeclaredConstructor().newInstance();
        t.setConfig(config);
        return t

This creates a warning "Unchecked call to getDeclaredConstructor(Class..) as a member of raw type 'java.lang.Class' '

I am looking for a better way to fix this deprecated call.

This code was not written by me. It provides a Java SOAP connection to Salesforce.com. I have written my own code to use it with Java 8, however, I thought it would be useful to update the code to work with Java 9+

Des Albert
  • 281
  • 2
  • 5
  • 17
  • 1
    There doesn't seem to be any deprecation [here](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/Constructor.html#newInstance(java.lang.Object...)), but rather a raw method invocation. See [here](https://stackoverflow.com/questions/2238818/java-unchecked-call-to-getconstructorjava-lang-class). – Mena May 10 '19 at 13:54
  • 1
    Please read "How to create a [mcve]". Then use the [edit] link to improve your question (do not add more information via comments). Otherwise we are not able to answer your question and help you. Please show the exact code you are using, with the exact compiler error/warning message. – GhostCat May 10 '19 at 13:56
  • 3
    Using `Class.get[Declared]Constructor().newInstance()` is the proper replacement for `Class.newInstance()`, so there is no "better way". Your problem is related to _raw types_. In other words, you're using `Class` instead of `Class` where `T` is the proper type. – Slaw May 10 '19 at 14:04
  • See https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it then ... – GhostCat May 10 '19 at 14:12
  • I have fixed the link and added that Transport is an interface, not a class – Des Albert May 10 '19 at 14:15
  • Whether `Transport` is an interface or a class is not important, because by `Class` I mean `java.lang.Class`. – Slaw May 10 '19 at 15:29

3 Answers3

9

Thanks for the excellent suggestions

I have applied the recommendations in the following way to make it a little easier to read

        Class<?> transClass = config.getTransport();
        Transport t = (Transport) transClass.getDeclaredConstructor().newInstance();
        t.setConfig(this);
Des Albert
  • 281
  • 2
  • 5
  • 17
2

As stated by the deprecation message of Class#newInstance(), the correct replacement is to use:

Followed by:

There is no "better way" because what you have is already the proper solution. The issue you have is related to raw types. Looking at the GitHub link you've provided, I'm assuming that config is an instance of ConnectorConfig. If that's correct, then unfortunately getTransport() returns Class and not Class<?> or Class<? extends Transport>. This is a problem with the API of the library you're using; you may want to consider submitting an issue if one doesn't already exist.

The call to getDeclaredConstructor is unchecked because it returns Constructor<T>—where T is from the generic parameter of Class—but you have a raw Class. You have two options to get rid of the warning:

  1. Cast the Class object to Class<?>.

    Transport t = (Transport) ((Class<?>) config.getTransport()).getDeclaredConstructor().newInstance();
    
  2. Use @SuppressWarnings("unchecked") in the most narrow scope possible.

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

The reason using Class#newInstance didn't cause this "unchecked call" warning is because that method returns T which, when Class is raw, simply resolves to Object—a non-generic type.

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • The GitHub library is relatively old so I doubt that it is maintained by SalesForce.com who have since moved away from SOAP interfaces. Nevertheless it works and I can continue trying to update it to be compatible with Java 9+ – Des Albert May 10 '19 at 19:57
  • I submitted a pull request with these changes to https://github.com/forcedotcom/wsc – Des Albert May 11 '19 at 21:28
2

There are two further aspects that need to be considered when following the recommendation in the Javadoc by replacing:

clz.newInstance()

with:

clz.getDeclaredConstructor().newInstance()

Firstly, Class#getDeclaredConstructor may throw InvocationTargetException or NoSuchMethodException (both forms of ReflectiveOperationException), where as Class#newInstance throws InstantiationException for these conditions.

As these are not types of RuntimeException, they need to be explicitly handled, arguably by catching them and setting them as a cause for a (new) InstantiationException that can be thrown, to preserve the signature for the calling code.

Secondly, Class#getDeclaredConstructor can cause an additional "accessDeclaredMembers" security check to be made (as well as the checkPackageAccess() check that Class#newInstance also makes).

Therefore, additional steps (such as the use of AccessController#doPrivileged) may need to be taken to ensure the caller does not fail this additional check.

So, a method like this:

Object createInstance(Class clz) 
        throws InstantiationException, IllegalAccessException { 
    return clz.newInstance();
}

might, once correctly modified, look something more like this:

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;
    }
}
ngmr
  • 21
  • 3