I have the following method which I know needs to be tweaked in order to do what I want:

Once the HashMap is loaded I want to be able to create multiple instances of the Runnable object SearchWorkerThread. Subclasses of SearchWorkerThread can be stored in the HashMap. Basically, I'm trying to have it setup so that I can grab any one of them and do this:

The method needs to be re-written so that a person doesn't call it like this: setCustomSearchRunnable("specialRunnable", new CustomWorkerThread());

How does the method need to be written so that the second parameter receives a reference to the class CustomWorkerThread? Not a new instance of the class CustomWorkerThread. Please advise.

It's not entirely clear to me what you're asking, but this may be on the right track, or at least, it may let you know where my thinking is at and thereby inspire you to clarify what you're asking.

Give a fully qualified class name as a String, we can do

And given a Class object, if the class in question is not abstract or an interface, and if we know it has a public, no-arg constructor, we can instantiate it like so:

I've left out the bits about generics, casting, exception handling, etc. until we see if this is really what you're looking for, and if you have questions about those areas.

Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237

posted Feb 29, 2012 12:26:28

0

Yeah... I think your on the right track...

The abstract class SearchWorkerThread is intended for another developer to create a subclass. For the sake of the example I was using the name CustomWorkerThread as one class that extends SearchWorkerThread. In my application I am allowing developers to create plugins. Once thieir plugin is completed, they should be able initialize their plugin while calling the method setCustomSearchRunnable(...).

So, based on what you're showing me, should my method look something more like:

What I've seen of the requirements don't tell me whether your suggestion would be possible or not. It's certainly preferable to use ordinary constructors rather than Class.newInstance() where possible, but whether that works for Alan, I can't tell.

What I've seen of the requirements don't tell me whether your suggestion would be possible or not. It's certainly preferable to use ordinary constructors rather than Class.newInstance() where possible, but whether that works for Alan, I can't tell.

I'm just trying to think of a case where we know the class name at compile time, an therefore could use Foo.class.newInstance(), but would not be able to just use new Foo() instead, and I'm drawing a total blank.

EDIT: Okay, maybe I could see a scenario. It's kind of a twist on the Prototype pattern. We know a fixed set of class names at compile time, but not which one will be needed at a given time, the Class objects end up as values in a Map, or elements in an array. Then we use the given Class object to tell us which of the fixed set of classes to instantiate at the appropriate time:

This code says "Hey, some time ago I created an object of type T and you stored it in your map. Now give it back to me, and don't make me cast it to T."

Here's the code which puts objects into the map:

So this setter method is passed an object of type T, as you suggest. But the getter method only needs the type, not an object. (Really it's just a cheap generics hack to remove a cast from a couple of dozen pieces of code.)

It's also possible that the classes in question don't have a no-args constructor, but a constructor which has some predetermined signature, and the parameters for that constructor will be supplied by the factory class, rather than the caller. In this case the caller provides only the class and the factory creates an object of that class using its secret data for the constructor parameters.

Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237

posted Mar 01, 2012 11:44:33

0

OK fellas, you're confusing me even more. I'm not that up on generics, so some of the code is throwing me. I'll tell you what I've been finding so far. I wrote my method like so at first:

In my test plugin I created a class named CustomWorkerThread which extends SearchWorkerThread. My plugin then calls this method like so:

So far, so good, right? Wrong! This results with a compile warning: The method setCustomSearchRunnable(String, Class<SearchWorkerThread>) in the type MainGUI is not applicable for the arguments (String, Class<CustomSearchWorkerThread>)

So I said, OK lets get a little more clever and change the Class type to something more generic like:

Well, this certainly helped to get rid of the warning message. But when I run this, the check for if(clazz.isInstance(new SearchWorkerThread())) wound up throwing the IllegalArgumentException. I didn't expect that. CustomSearchWorkerThread is a subclass of SearchWorkerThread! So, how am I supposed to test that the class being passed is a subclass of SearchWorkerThread?

Alan

Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237

posted Mar 01, 2012 11:56:42

0

Hey! I think I found the answer. I just discovered the Class has a method called getSuperClass(). So I tried it:

Compiled fine...runtime ran without errors...I think that's it! If any of you can think of another better way to write this I'm interested.

You were asking whether a SearchWorkerThread object could be cast to the type you were thinking of, which presumably would have been a subclass of SearchWorkerThread. The answer of course is no, you can't do that. But I expect you really wanted to know whether an object of the type you were thinking of could be cast to the type SearchWorkerThread. So you had your test backwards.

The new test says "Is the class I'm thinking of a direct subclass of SearchWorkerThread?" which I think is too restrictive. Shouldn't you be allowing any class which descends from SearchWorkerThread? Perhaps the isAssignableFrom method would be more suitable; that would also remove the need for generating throwaway objects.

And "public void setCustomSearchRunnable(String key, Class<SearchWorkerThread> clazz)" is somewhat pointless because the second parameter is simply the SearchWorkerThread.class literal. And since you actually want to pass in subclasses of SearchWorkerThread, it's also wrong. Perhaps you meant this:

Alan, your sixth line should be probably changed to this:
It checks that an instance of class clazz can be assigned to a variable declared as SearchWorkerThread - which is exactly what you wanted.

Note: the isInstance method would also work, but you'd have to swap the classes like this:
- it works nearly the same, but creates a new instance from clazz needlessly (only if clazz was null, these two approaches would yield different behavior).

I always tend to confuse the meaning of the Class instance and the parameter when using the isInstance and isAssignableFrom methods. I usually have to chew a few minutes over these function's Javadoc before I figure out which class goes first and which goes as a parameter... there must be a burnt-out neuron or two in my neocortex.

Edit: my isAssignableFrom comprehension deficiency is a real handicap! While I was pondering it for the umpteenth time in my Java carrier, Paul has provided his own - and much better - answer.

Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237

posted Mar 02, 2012 18:58:47

0

This is really great stuff guys. I've learned a lot from this. I'll try out your suggestions and see where it takes me.