• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

a question on parameter passing

 
Ranch Hand
Posts: 237
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 237
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Marshal
Posts: 28193
95
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Posts: 237
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Fantastic guys. I'll work with this.

Thanks,

Alan
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Marshal
Posts: 28193
95
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Marshal
Posts: 28193
95
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 237
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 237
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Marshal
Posts: 28193
95
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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:
 
Sheriff
Posts: 3837
66
Netbeans IDE Oracle Firefox Browser
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 237
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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 can't beleive you just said that. Now I need to calm down with this tiny ad:
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic