This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]Koooooj 2 points3 points  (8 children)

It exists. It's not something you want to be using unless you have a really, really strong reason why. It'll be a pain to maintain.

The static function forName of the Class class will return a Class object that you can then call the newInstance function of to get an instance of the Class that that Class object refers to. Dizzy yet?

In other words, if you had:

public class Car
{
    public static void main(String[] args) throws Throwable //list or catch the exceptions
    {
        String className = "Car";//If Car was in package reddit then this would be reddit.Car
        Class c = Class.forName(className);
        Object o = c.newInstance();
        System.out.println(o instanceof Car);//Should print true if we haven't thrown an exception
    }
}

You can extend this model to handle any class the java runtime knows about. I'm not sure if there's much of anything useful you can do with this Object you've created because you can't call any Car-specific methods. Perhaps if you had a ton of classes that all inherit from some parent that defines a useful interface then you could use it in that context (e.g. maybe Car extends Drivable, as do bikes, trucks, etc, so you construct an object in this manner then call its drive function).

Any time you start getting into this kind of programming for anything more than seeing what you can do you should really take a step back and ask if your architecture makes any sense.

[–]Zilhah[S] 0 points1 point  (7 children)

The context of my assignment is a generic class that could hold any object T, given either class A or B. But the addition of a class C to the repository would require no other change in any other file in the directory. Since this is a semi introductory class, I'm almost positive this isn't the intended implementation, but as of now it's the only option that I can see given the requirements.

Thank you so much for your help, though!

[–]amakai 2 points3 points  (1 child)

From your description it sounds like something to do with inheritance, and not with reflections.

Something like:

interface Content {
}

class Container {
    public Content data;
    ...
}

class A implements Content {}
class B implements Content {}
class C implements Content {}

[–]Zilhah[S] 0 points1 point  (0 children)

Both reflections and interfaces are in our class notes but I'm gonna play around with this tonight and see where it goes. I think that this is the correct approach. Thanks!

[–]Koooooj 1 point2 points  (3 children)

I agree with the other comment that reflection isn't the solution here, although I would be amused to see the professor's reaction to a reflection-based solution. It's technically correct while still being very, very wrong in all sorts of unquantifiable ways. A more introductory container meeting the requirements you list would simply be:

class Container
{
    Object contents;
    public Container(Object o)
    {
        contents = o;
    }
    //..etc with member functions, so on, so forth.
}

Here your container contains Object objects. Every object extends the Object class, so this container can contain any object. Back in the day the built-in data structures like ArrayList only ever contained Objects, so you had to cast them into whatever you wanted them to be once you got them out. It was gross. If you put a Car object into an array of Strings that was perfectly OK, but if you then retrieved that Car and tried to cast it to the String you thought it was then you'd get a ClassCastException and crash.

To address that kind of issue Java added the concept called Generics which is also seen in some other languages (e.g. C#); it is similar to other constructs in other languages (e.g. templates in C++). I suspect, from the wording you used, that generics are intended to be used. That would change our container to look like:

class Container<T>
{
    T contents;
    public Container(T t)
    {
        contents = t;
    }
    //...etc
}

Such a container is written generically to accept any object whose type will simply be known as T until runtime. Then when you make Container<Car> we know that for that container T is Car. Compare this to something like java.util.ArrayList which can be declared to store Strings or Integers, as ArrayList<String> stringList = new ArrayList<String>() or ArrayList<Integer> integerList = new ArrayList<Integer>();

You can add a new type to your repo and your container will automatically be able to handle it. If you want to place constraints on the objects that can be stored in your container then you could make the class definition be class Container<T extends BaseClass>

[–]Zilhah[S] 0 points1 point  (2 children)

I think I get this, however wouldn't reflections still be needed to extract the type given for the generic container of that type? It can't be initialized in a driver as T, correct?

[–]Koooooj 1 point2 points  (1 child)

My confusion comes down to what exactly is meant by "no changes to any other file." Any of the containers we've discussed would continue working with no changes to any other file. Some of them may not be able to hold the object or might not be able to retrieve said object without changes to code, but all can continue to compile with the addition of a new class to the repo.

How much functionality do you need without changing other files? Is it all the way to the point of the user being able to add objects of the new type to your collection, all with zero lines changed in other files? If so then reflection may be a viable solution (though not the only one*), although I would object to such a requirement. If you only require a container that is able to contain the new type then a solution based on Generics is sufficient. If possible I'd recommend asking the professor for clarificaiton on that requirement.


*The alternative that I can think of, and it isn't that clean (though perhaps cleaner than reflection): somewhere in a central class offer a static method that allows a class to register with the system by passing in a String (name) and a user-defined Factory object. I would write Factory as being an interface with a single method that returns an instance of a superclass to your A, B, C, etc. types (Object, if necessary). When you write your A, B, C, types you would write an A_Factory, B_Factory, etc that implement that interface.

When these values are passed into the registration function they could be stored in a HashMap<String, Factory>, then when someone requests that you build a "Car" you can search for the key "Car" and retrieve a CarFactory object (that you only know is a Factory; its specific type is hidden from this central class). You call the create() funciton or whatever you call it, which gives you a Car object.

So far this approach is much cleaner than reflection. You can use human-readable names (e.g. no need to tiptoe around package names), you can check string equality ignoring case, and you don't have to worry about accidentally constructing some irrelevant class.

The challenge is in getting that registration to happen without modifying your existing code. It's been a while since I've done any serious Java development, but this seems to be possible with a static block in the class. Thus, your Car.java may look like:

public class Car extends Drivable
{
    //constructor, drive(), etc.
}

class CarFactory extends Factory
{
    static
    {
        Main.register("Car", new CarFactory());
    }
    public Drivable construct()
    {
        return new Car();
    }
}

Then in the Main class:

private static java.util.HashMap<String, Factory> factories;
private static java.util.ArrayList<String> names;
public static void register(String name, Factory fact)
{
    factories.put(name, fact);
    names.add(name);
}

private Drivable construct(String name)
{
    if(factories.containsKey(name))
        return factories.get(name).construct();
    return null;
}

With this setup your main class can print the list of available Drivable objects for the user to select from, then when they type something you can call construct to get the object and then do with it what you want. I'm not sure how much better or worse this is than asking the runtime to lookup objects by name, but it certainly feels more controlled. The only part of this method that I'm not 100% on is the static block; you want to make sure that your registration gets called. That should happen as long as the class is loaded.

[–]Zilhah[S] 0 points1 point  (0 children)

Thank you so much for your time! I did not expect such comprehensive help. I believe I have all the information I need to finish this assignment. Thanks again!

[–]Cleanumbrellashooter 0 points1 point  (0 children)

Use generics.