wood burning stoves 2.0*
The moose likes Java in General and the fly likes a question on parameter passing Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "a question on parameter passing" Watch "a question on parameter passing" New topic
Author

a question on parameter passing

Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237
Hi there,

I need a little guidance on how to pass a reference of a Class of a particular type to a method so that I can then make several instances of it. I have a HashMap in my program that looks like so:

protected Map<String, SearchWorkerThread> customSearchRunnables = Collections.synchronizedMap(new HashMap<String, SearchWorkerThread>());

SearchWorkerThread is an abstract class that implements Runnable.

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.

Alan
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

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
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:


Then they would call the method like:
Class<SearchWorkerThread> theClass = Class.forName("CustomWorkerThread");
setCustomSearchRunnable("specialRunnable", theClass) ;

Instantiating the class? Maybe:

SearchWorkerThread runnable1 = (SearchWorkerThread)customSearchRunnables.get(this.customRunnableKeySelected).getInstance();

Am I on the right track here?

Alan
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18541
    
    8

Alan Shiers wrote:Class<SearchWorkerThread> theClass = Class.forName("CustomWorkerThread");


I guess you're on the right track, but if your subclasses are already all known at compile time, then that's the hard way to do it. Just use the "class" literal of the class:

and make the compiler catch your spelling errors. Using Class.forName leaves the spelling errors to cause exceptions at run-time.
Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237
Fantastic guys. I'll work with this.

Thanks,

Alan
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

Paul Clapham wrote:
Alan Shiers wrote:Class<SearchWorkerThread> theClass = Class.forName("CustomWorkerThread");


I guess you're on the right track, but if your subclasses are already all known at compile time, then that's the hard way to do it. Just use the "class" literal of the class:

and make the compiler catch your spelling errors. Using Class.forName leaves the spelling errors to cause exceptions at run-time.


Good point.

However, if the class name is known at compile time, then why not just do


instead of


?

Or am I missing something in the OP's requirements?
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18541
    
    8

Jeff Verdegan wrote:Or am I missing something in the OP's requirements?


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.
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

Paul Clapham wrote:
Jeff Verdegan wrote:Or am I missing something in the OP's requirements?


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:




Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18541
    
    8

Here's an example of some code I wrote a while ago:


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
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:

setCustomSearchRunnable("CustomSearch", CustomSearchWorkerThread.class);

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
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.

Alan
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18541
    
    8

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:
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3606
    
  60

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
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.

Alan
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: a question on parameter passing
 
Similar Threads
Synchronized Block
[newbie] invoking methods within a class (java.lang.reflect)
Need help understanding results from running 2 threads
synchronized blocks for static and non-static methods
Runnable object use