Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Java (http://www.velocityreviews.com/forums/f30-java.html)
-   -   Yet another generics question: Needs unchecked conversion to conform to ... (http://www.velocityreviews.com/forums/t389741-yet-another-generics-question-needs-unchecked-conversion-to-conform-to.html)

Michel T. 12-29-2006 09:31 PM

Yet another generics question: Needs unchecked conversion to conform to ...
 
I spent a lot of time massaging the code below to avoid a warning,
without success.

I pasted a sample class and inner classes that shows what I want to do.
I wrote two versions of a factory method: one that does not generate
any warnings, and the other generates one.
The return type of the second method is defined like this:

BeanFactory<Bean> createFactory2(String className){
...
}

The returned BeanFactory is also parametized, and this is where my
problem lies. Can anyone suggest how to avoid the warning, besides
adding a @suppresswarning annotation?

public class Generics {

public BeanFactory createFactory1(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});

return cstr.newInstance(new Object[]{});
}

public BeanFactory<Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});

// The following line causes the warning:
return cstr.newInstance(new Object[]{});
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}


Tom Hawtin 12-30-2006 12:48 AM

Re: Yet another generics question: Needs unchecked conversion toconform to ...
 
Michel T. wrote:

>
> public BeanFactory<Bean> createFactory2(String beanClass)
> throws Exception{
>
> Class<?> bc = Class.forName(beanClass);
>
> Class<? extends BeanFactory> bfc =
> bc.asSubclass(BeanFactory.class);
>
> Constructor<? extends BeanFactory> cstr =
> bfc.getConstructor(new Class[]{});
>
> // The following line causes the warning:
> return cstr.newInstance(new Object[]{});
> }
>
> public class BeanFactory<T extends Bean>{
> }


My first suggestion is not to use reflection.

If not, does BeanFactory<? extends Bean> work for you? (There is no
guarantee that the T in a subclass of BeanFactory is exactly Bean.)

Alternatively, create a bean factory factory. (But stick with using a
Constructor rather than evil Class.newInstance.)

Tom Hawtin

Michel T. 12-30-2006 02:31 AM

Re: Yet another generics question: Needs unchecked conversion to conform to ...
 
> My first suggestion is not to use reflection.

I have to use reflection. This example is a very-reduced sample that
does not reflect the reality where this is required. I created this
simple class to show the warning.

> If not, does BeanFactory<? extends Bean> work for you? (There is no
> guarantee that the T in a subclass of BeanFactory is exactly Bean.)


I would not mind changing the return value of createFactory2 to

BeanFactory<? extends Bean> createFactory2(String beanClass)

but I get the same warning.


Also, I tried the following but it does not compile (thus it must not
make sense either):

public BeanFactory<? extends Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);

Class<? extends BeanFactory<? extends Bean>> bfc =
bc.asSubclass(BeanFactory.class);

Constructor<? extends BeanFactory<? extends Bean>> cstr =
bfc.getConstructor(new Class[]{});

return cstr.newInstance(new Object[]{});
}


>
> Alternatively, create a bean factory factory. (But stick with using a
> Constructor rather than evil Class.newInstance.)


I must use reflection to instantiate the factory. Otherwise, I would
not be posting that question ;)

--
Michel T.


Daniel Pitts 12-30-2006 07:22 AM

Re: Yet another generics question: Needs unchecked conversion to conform to ...
 

Michel T. wrote:
> > My first suggestion is not to use reflection.

>
> I have to use reflection. This example is a very-reduced sample that
> does not reflect the reality where this is required. I created this
> simple class to show the warning.
>
> > If not, does BeanFactory<? extends Bean> work for you? (There is no
> > guarantee that the T in a subclass of BeanFactory is exactly Bean.)

>
> I would not mind changing the return value of createFactory2 to
>
> BeanFactory<? extends Bean> createFactory2(String beanClass)
>
> but I get the same warning.
>
>
> Also, I tried the following but it does not compile (thus it must not
> make sense either):
>
> public BeanFactory<? extends Bean> createFactory2(String beanClass)
> throws Exception{
>
> Class<?> bc = Class.forName(beanClass);
>
> Class<? extends BeanFactory<? extends Bean>> bfc =
> bc.asSubclass(BeanFactory.class);
>
> Constructor<? extends BeanFactory<? extends Bean>> cstr =
> bfc.getConstructor(new Class[]{});
>
> return cstr.newInstance(new Object[]{});
> }
>
>
> >
> > Alternatively, create a bean factory factory. (But stick with using a
> > Constructor rather than evil Class.newInstance.)

>
> I must use reflection to instantiate the factory. Otherwise, I would
> not be posting that question ;)
>
> --
> Michel T.


*Why* do you need to use reflection? I've found that in most
circumstances, you don't need to use it, even if you *think* you do.

Moving forward, assuming that you do indeed need to do it this way...
what is the exact warning you get? Can you post an sscce
<http://physci.org/codes/sscce/> that allows us to reproduce the
problem?

Anyway, good luck.


Michel T. 12-30-2006 02:20 PM

Re: Yet another generics question: Needs unchecked conversion to conform to ...
 
Thanks for your replies,

Daniel Pitts a écrit :
> *Why* do you need to use reflection? I've found that in most
> circumstances, you don't need to use it, even if you *think* you do.


I'll explain in an upcoming separate message.

> Moving forward, assuming that you do indeed need to do it this way...
> what is the exact warning you get? Can you post an sscce
> <http://physci.org/codes/sscce/> that allows us to reproduce the
> problem?


The exat warning is:

Type safety: The expression of type capture-of ? extends
Generics.BeanFactory needs unchecked conversion to conform to
Generics.BeanFactory<? extends Generics.Bean>

The "ssce" (thank you for the info on this acronym) was there in the
first message: The "Generics" class compiles as is without any
dependanies, and the warning shows up in Eclipse (I did not try on any
other compiler). Here it is again with a slight modification in the
return type of the createFactory2 method :

public class Generics {

public BeanFactory createFactory1(String beanClass) throws Exception{

Class<?> bc = Class.forName(beanClass);
Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});
return cstr.newInstance(new Object[]{});
}

public BeanFactory<? extends Bean> createFactory2(String beanClass)
throws Exception{

Class<?> bc = Class.forName(beanClass);
Class<? extends BeanFactory> bfc =
bc.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> cstr =
bfc.getConstructor(new Class[]{});
// The following line causes a warning
return cstr.newInstance(new Object[]{});
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}


Daniel Pitts 12-30-2006 06:24 PM

Re: Yet another generics question: Needs unchecked conversion to conform to ...
 

Michel T. wrote:
> Thanks for your replies,
>
> Daniel Pitts a écrit :
> > *Why* do you need to use reflection? I've found that in most
> > circumstances, you don't need to use it, even if you *think* you do.

>
> I'll explain in an upcoming separate message.
>
> > Moving forward, assuming that you do indeed need to do it this way...
> > what is the exact warning you get? Can you post an sscce
> > <http://physci.org/codes/sscce/> that allows us to reproduce the
> > problem?

>
> The exat warning is:
>
> Type safety: The expression of type capture-of ? extends
> Generics.BeanFactory needs unchecked conversion to conform to
> Generics.BeanFactory<? extends Generics.Bean>
>
> The "ssce" (thank you for the info on this acronym) was there in the
> first message: The "Generics" class compiles as is without any
> dependanies, and the warning shows up in Eclipse (I did not try on any
> other compiler). Here it is again with a slight modification in the
> return type of the createFactory2 method :
>
> public class Generics {
>
> public BeanFactory createFactory1(String beanClass) throws Exception{
>
> Class<?> bc = Class.forName(beanClass);
> Class<? extends BeanFactory> bfc =
> bc.asSubclass(BeanFactory.class);
> Constructor<? extends BeanFactory> cstr =
> bfc.getConstructor(new Class[]{});
> return cstr.newInstance(new Object[]{});
> }
>
> public BeanFactory<? extends Bean> createFactory2(String beanClass)
> throws Exception{
>
> Class<?> bc = Class.forName(beanClass);
> Class<? extends BeanFactory> bfc =
> bc.asSubclass(BeanFactory.class);
> Constructor<? extends BeanFactory> cstr =
> bfc.getConstructor(new Class[]{});
> // The following line causes a warning
> return cstr.newInstance(new Object[]{});
> }
>
> public class BeanFactory<T extends Bean>{
> }
>
> public class Bean{
> }
> }


Oh, okay.

There isn't a way to remove all warnings in this case.
Whenever you use reflection to create an object, erasure prevents
anyone from knowing what generic type it has. You'll have to use a
@SupressWarning, or just live with the warning.


Tom Hawtin 12-30-2006 08:13 PM

Re: Yet another generics question: Needs unchecked conversion toconform to ...
 
Michel T. wrote:
>> My first suggestion is not to use reflection.

>
> I have to use reflection. This example is a very-reduced sample that
> does not reflect the reality where this is required. I created this
> simple class to show the warning.
>
>> If not, does BeanFactory<? extends Bean> work for you? (There is no
>> guarantee that the T in a subclass of BeanFactory is exactly Bean.)

>
> I would not mind changing the return value of createFactory2 to
>
> BeanFactory<? extends Bean> createFactory2(String beanClass)
>
> but I get the same warning.


Rather strangely it requires an intermediate variable. I have no idea
why. Captures are odd.

import java.lang.reflect.*;

public class Generics {
public BeanFactory<?> createFactory(
String beanClassName
) throws
NullPointerException,
LinkageError,
ExceptionInInitializerError,
ClassNotFoundException,
ClassCastException,
NoSuchMethodException,
SecurityException,
InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Class<?> rawClass = Class.forName(beanClass);
Class<? extends BeanFactory> clazz =
rawClass.asSubclass(BeanFactory.class);
Constructor<? extends BeanFactory> ctor =
clazz.getConstructor(new Class[] { });
BeanFactory erased = ctor.newInstance(new Object[] { });
BeanFactory<?> reified = erased;
return reified;
}

public class BeanFactory<T extends Bean>{
}

public class Bean{
}
}

>> Alternatively, create a bean factory factory. (But stick with using a
>> Constructor rather than evil Class.newInstance.)

>
> I must use reflection to instantiate the factory. Otherwise, I would
> not be posting that question ;)


Why must?

What I mean was to create something like:

class BeanFactoryFactory {
public BeanFactory<?> createFactory();
}

And Generics.createFactory would become:

public BeanFactory<?> createFactory(String beanClassName) {
BeanFactoryFactory factoryFactory = ... beanClassName ...
return factoryFactory.createFactory();
}

Tom Hawtin

Michel T. 12-31-2006 01:07 AM

Re: Yet another generics question: Needs unchecked conversion to conform to ...
 
> Rather strangely it requires an intermediate variable. I have no idea
> why. Captures are odd.

I tried and you are right, it got rid of the warning. But doing so, I
had changed the signature of the method that broke the client code
elsewhere. So I am back to the original version of the method that has
this signature:

BeanFactory<Bean> createFactory2(Strig name)

> > I must use reflection to instantiate the factory. Otherwise, I would
> > not be posting that question ;)

>
> Why must?


Here is a more complete example that explains in the comments why I am
doing all of this. If you enjoy generics enough to take a look at it
and comment, I'll be more than impressed!

import java.lang.reflect.Constructor;

public class Generics {

/**
* this code will be used in either an OSGi container,
* or in a normal java application.
* So Service implementations are loaded either from
* the OSGi bundle context (when in an OSGi container)
* or from the current class loader using reflection.
* The service finder interface allows to locate the
* Service implementation using a different strategy depending
* on the runtime context.
* Only the reflection strategy is shown here.
* Experimenting with it, I found that I would
* not have to define generices in the interfaces and class
* if I did not have this method:
*
* public void doSomethingWith(T component);
*
* in the Service interface. Wanting to allow a generic type
* as a parameter appears to force me to put generics everywhere.
*/
public static interface ServiceFinder<T extends ServiceComponent> {
public Service<ServiceComponent> find(String id) throws Exception;
}

public static class ServiceFinderUsingReflection
implements ServiceFinder<ServiceComponent> {

public Service<ServiceComponent> find(String serviceId)
throws Exception{

// in real life, the service class name
// is computed differently using
// a pattern or xml config file
String serviceClass = serviceId;

Class<?> bc = Class.forName(serviceClass);
Class<? extends Service> bfc = bc.asSubclass(Service.class);

Constructor<? extends Service> cstr =
bfc.getConstructor(new Class[]{});
Service<?> ret = cstr.newInstance(new Object[]{});

// This is the warning I'd like to get rid of
return (Service<ServiceComponent>)ret;
}
}

public static interface Service<T extends ServiceComponent>{

public T createServiceComponent();

/**
* Using generics here allows implementation class to
* override using their own types.
*/
public void doSomethingWith(T component);
}

public static abstract class ServiceComponent{
}


// ------------------------------------
// Sub-system external implementation
// of Bean and BeanFactory

public static class ServiceComponentX extends ServiceComponent {
public void doIt(){
}
}

public static class ServiceX implements Service<ServiceComponentX>{

/**
* Here, using generics, I can define methods that expect
* a ServiceComponentX.
*/
public void doSomethingWith(ServiceComponentX c) {
c.doIt();
}

public ServiceComponentX createServiceComponent() {
return new ServiceComponentX();
}
}
// ------------------------------------

public static void main(String[] args) throws Exception {


// Use the reflection finder. We may also have chosen to
// use, say, an OSGi-based finder that locates the service
// using the OSGi bundle context.
ServiceFinder<ServiceComponent> finder =
new ServiceFinderUsingReflection();

// Locate the service identified by the service id "ServiceX".
// In this example, it happens to be the class name but in real
// life, it is not the class name, but an ID from which the finder
// implementation uses to locate the real service.
Service<ServiceComponent> service = finder.find("ServiceX");

ServiceComponent component = service.createServiceComponent();
// Setup the component state here
// ...

// once it is setup,
service.doSomethingWith(component);
}
}


Tom Hawtin 12-31-2006 01:12 PM

Re: Yet another generics question: Needs unchecked conversion toconform to ...
 
Michel T. wrote:
>> Rather strangely it requires an intermediate variable. I have no idea
>> why. Captures are odd.

> I tried and you are right, it got rid of the warning. But doing so, I
> had changed the signature of the method that broke the client code
> elsewhere. So I am back to the original version of the method that has
> this signature:
>
> BeanFactory<Bean> createFactory2(Strig name)


You have not and in general cannot verify that the object is of that
type. The signature is wrong. For instance, in your example ServiceX is
not a Service<ServiceComponent> but it is a Service<? extends
ServiceComponent>.

Generally a good move when using reflection and generics, is to add a
layer of indirection, so you don't have to mix the two.

So instead of loading Service<? extends ServiceComponent>, load a
ServiceProvider:

interface ServiceProvider {
<T extends ServiceComponent> Service<T> find(
Class<T> componentClass
);
}

class ServiceProviderX implements ServiceProvider {
@SuppressWarnings("unchecked")
<T extends ServiceComponent> Service<T> find(
Class<T> componentClass
) {
if (componentClass != ServiceComponentX.class) {
throw new SomeException();
}
return (Service<T>)new ServiceX();
}
}

I think you are stuck with the unchecked warning there, but at least the
code is now sound. Alternatively, add an asSubclass-like method to Service.

Really, we want a Service interface for each type of ServiceComponent. So:

public interface ServiceComponent {
}
public interface Service<T extends ServiceComponent> {
T createServiceComponent();
void doSomethingWith(T component);
}
public interface XServiceComponent implements ServiceComponent {
}
public interface XService extends Service<XServiceComponent> {
}
class XServiceComponentImpl implements XServiceComponent {
}
public class XServiceImpl implements XService {
public XServiceComponent createServiceComponent() {
return new XServiceComponentImpl();
}
public void doSomethingWith(XServiceComponent component) {
...
}
}
....
public <T extends Service<?>> find(
Class<T> clazz, String serviceClassName
) throws
NullPointerException,
LinkageError,
ExceptionInInitializerError,
ClassNotFoundException,
ClassCastException,
NoSuchMethodException,
SecurityException,
InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
...
return clazz.cast(service);
}
....
// Or: ServiceX service = find(
Service<XServiceComponent> service = find(
XService.class, className
);

Tom Hawtin

Michel T. 12-31-2006 03:01 PM

Re: Yet another generics question: Needs unchecked conversion to conform to ...
 
Tim, thank you very much for the feedback.

> You have not and in general cannot verify that the object is of that
> type. The signature is wrong. For instance, in your example ServiceX is
> not a Service<ServiceComponent> but it is a Service<? extends
> ServiceComponent>.


But since ServiceComponent is an abstract class or interface, in
practice, is'nt Service<? extends ServiceComponent> a "subset" of
Service<ServiceComponent> ? Therefore, is'nt the cast from the later
into the former safe?

> Generally a good move when using reflection and generics, is to add a
> layer of indirection, so you don't have to mix the two.


On the other hand, it seems more work for the service implementation
(ServiceX) who would have to provide two interfaces, and matching
implementation classes. I may be missing a point though.

I updated my test class to follow some of your recommandation. this is
whet I end up with.

import java.lang.reflect.Constructor;

public class Generics2 {

static interface ServiceProvider {
<T extends ServiceComponent> Service<T> find(
String className)
throws Exception;
}

static class ServiceProviderWithReflection implements ServiceProvider
{
@SuppressWarnings("unchecked")
public <T extends ServiceComponent> Service<T> find(
String className) throws Exception {
Class<?> bc = Class.forName(className);
Class<? extends Service> bfc = bc.asSubclass(Service.class);

Constructor<? extends Service> cstr =
bfc.getConstructor(new Class[]{});
Service<?> ret = cstr.newInstance(new Object[]{});

// It seems we will have to live with this warning
return (Service<T>)ret;
}
}

static public interface ServiceComponent {
}

static public interface Service<T extends ServiceComponent> {
T createServiceComponent();
void doSomethingWith(T component);
}

static class ServiceComponentX implements ServiceComponent {
}

static public class ServiceX implements Service<ServiceComponentX> {
public ServiceComponentX createServiceComponent() {
return new ServiceComponentX();
}
public void doSomethingWith(ServiceComponentX component) {

}
}
public static void main(String[] args) throws Exception {


// Use the reflection finder. We may also have chosen to
// use, say, an OSGi-based finder that locates the service
// using the OSGi bundle context.
ServiceProvider finder = new ServiceProviderWithReflection();

// Locate the service identified by the service id "ServiceX".
// In this example, it happens to be the class name but in real
// life, it is not the class name, but an ID from which the finder
// implementation uses to locate the real service.
Service<ServiceComponent> service =
finder.find(Generics2.ServiceX.class.getName());

ServiceComponent component = service.createServiceComponent();
// Setup the component state here
// ...

// once it is setup,
service.doSomethingWith(component);
}
}


--
Michel T.



All times are GMT. The time now is 04:12 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.