I am designing a virtual aquarium. I have a class: Fish which I inherit to create classes of different species. The user can select the species in a combo box and click a button to put the fish in the tank. I use the following code to create the fish:
switch(s){
case "Keegan" :
stock.add(new Keegan(this, x,y));
break;
case "GoldenBarb" :
stock.add(new GoldenBarb(this, x,y));
"stock" is a LinkedList and "s" is the String selected in the Jcombobox. As it stands I will have to create a long switch when I add a bunch of different species. I would like the code to look like:
stock.add(new s(this,x,y));
and dispense with the switch such that all I have to do is create the class and add its name to the combo box and have it work. Is there a way to do so? Any help is appreciated.
You want to use a bunch of factory objects, stored in a Map under the string keys that you use in the switch.
These are the classes for the various fish you should already have.
abstract class FishBase {}
class Keegan extends FishBase {
Keegan(Object _this, int x, int y) {
// ...
}
}
class GoldenBarb extends FishBase {
GoldenBarb(Object _this, int x, int y) {
// ...
}
}
An interface for all the fish factories. A fish factory represents a way to create some type of fish. You didn't mention what the constructor signature is so I just picked some types.
interface IFishFactory {
FishBase newFish(Object _this, int x, int y);
}
Set up one factory for every fish type. These obviously don't need to be anonymous classes, I'm using them to cut down on clutter.
Map<String, IFishFactory> fishFactories = new HashMap<>();
fishFactories.put("Keegan", new IFishFactory() {
public FishBase newFish(Object _this, int x, int y) {
return new Keegan(_this, x, y);
}
});
fishFactories.put("GoldenBarb", new IFishFactory() {
public FishBase newFish(Object _this, int x, int y) {
return new GoldenBarb(_this, x, y);
}
});
Then just pick the factory from the Map using the string you already have. You might want to check whether a factory for the given name exists.
stock.add(fishFactories.get(s).newFish(this, x, y));
Now, if all your fish classes have the exact same constructor signature, you can create a single factory class that can handle all of them using reflection, and get rid of some boilerplate.
class ReflectionFishFactory implements IFishFactory {
Constructor<? extends FishBase> fishCtor;
public ReflectionFishFactory(Class<? extends FishBase> fishClass)
throws NoSuchMethodException {
// Find the constructor with the parameters (Object, int, int)
fishCtor = fishClass.getConstructor(Object.class,
Integer.TYPE,
Integer.TYPE);
}
#Override
public FishBase newFish(Object _this, int x, int y) {
try {
return fishCtor.newInstance(_this, x, y);
} catch (InstantiationException
| InvocationTargetException
| IllegalAccessException e) {
// this is terrible error handling
throw new RuntimeException(e);
}
}
}
Then register it for every applicable subclass.
for (Class<? extends FishBase> fishClass :
Arrays.asList(Keegan.class,GoldenBarb.class)) {
fishFactories.put(fishClass.getSimpleName(),
new ReflectionFishFactory(fishClass));
}
I think reflection might be what you are looking for. This allows you to avoid the switch statement, which is what you are asking.
Reflection (among other things) allows you to run methods with just strings. So in Java, where you would normally call a method like this:
new Foo().hello();
With Reflection, you can use a string to call the method, like this:
Class<?> clazz = Class.forName("Foo");
clazz.getMethod("hello").invoke(clazz.newInstance());
Java Constructor Reflection example.
Regarding the Factory pattern (referring now to other answers), as I understand it, that is just encapsulating the switch statement (or whatever method you choose to use). The Factory pattern itself is not a means of avoiding the switch statement. The Factory Pattern is a good thing, but not what you were asking. (You will probably want to use the factory pattern in any case).
Let's go step by step to see how far you want to go.
First, you can abstract out the creation of fish in a FishFactory, so that the original place you do the switch statement can simply changed to
stock.add(fishFactory.createFish(s, x, y));
Then the switch case goes to the factory:
public class SimpleFishFactory {
#Override
public Fish createFish(String fishType, int x, int y) {
switch(s){
case "Keegan" :
return new Keegan(this, x,y);
break;
case "GoldenBarb" :
return new GoldenBarb(this, x,y);
//....
}
}
}
(I assume all your fish is having same interface/base class as Fish)
If you want to make the creation look more elegant, there are two common ways to choose from:
Reflection
Idea is simple. First setup a lookup table of string vs fish class (or constructor), and each createFish() is creating new instance of fish by reflection
public class ReflectionFishFactory {
private Map<String, Class<? extends Fish>> fishClasses = new HashMap<...>();
public ReflectionFishFactory() {
//set up fishClasses with name vs corresponding classes.
// you may read it from file, or hard coded or whatever
fishClasses.put("Keegan", Keegan.class);
fishClasses.put("GoldenBarb", GoldenBarb.class);
}
#Override
public Fish createFish(String fishType, int x, int y) {
Class<?> fishClass = fishClasses.get(fishType);
// use reflection to create new instance of fish by
// by using fishClass
}
}
Prototype Pattern
For some reason, you may not want to use reflection (maybe due to slowness of reflection, or different fishes have very different way to create), you may look into Prototype Pattern of GoF.
public class PrototypeFishFactory {
private Map<String, Fish> fishes = new HashMap<...>();
public ReflectionFishFactory() {
//set up fishClasses with name vs corresponding classes.
// you may read it from file, or hard coded or whatever
fishClasses.put("Keegan", new Keegan(....) );
fishClasses.put("GoldenBarb", new GoldenBarb(....) );
}
#Override
public Fish createFish(String fishType, int x, int y) {
return fishes.get(fishType).cloneNewInstance(x, y);
}
}
A combination of enums and factory strategies could be used for a simple, type-safe, way of creating object instances from Strings and for providing a set (or array) of Strings.
Take the follwoing eample -
import java.util.HashMap;
import java.util.Map;
public enum FishType {
BLUE_FISH(BlueFish.class, new FactoryStrategy<BlueFish>(){
public BlueFish createFish(int x, int y) {
return new BlueFish(x, y);
}}),
RED_FISH(RedFish.class, new FactoryStrategy<RedFish>(){
public RedFish createFish(int x, int y) {
//an example of the increased flexibility of the factory pattern - different types can have different constructors, etc.
RedFish fish = new RedFish();
fish.setX(x);
fish.setY(y);
fish.init();
return fish;
}});
private static final Map<Class<? extends Fish>, FactoryStrategy> FACTORY_STRATEGY_MAP = new HashMap<Class<? extends Fish>, FactoryStrategy>();
private static final String[] NAMES;
private FactoryStrategy factoryStrategy;
private Class<? extends Fish> fishClass;
static {
FishType[] types = FishType.values();
int numberOfTypes = types.length;
NAMES = new String[numberOfTypes];
for (int i = 0; i < numberOfTypes; i++) {
FishType type = types[i];
FACTORY_STRATEGY_MAP.put(type.fishClass, type.factoryStrategy);
NAMES[i] = type.name();
}
}
<F extends Fish> FishType(Class<F> fishClass, FactoryStrategy<F> factoryStrategy) {
this.fishClass = fishClass;
this.factoryStrategy = factoryStrategy;
}
public Fish create(int x, int y) {
return factoryStrategy.createFish(x, y);
}
public Class<? extends Fish> getFishClass() {
return fishClass;
}
public interface FactoryStrategy<F extends Fish> {
F createFish(int x, int y);
}
#SuppressWarnings("unchecked")
public static <F extends Fish> FactoryStrategy<F> getFactory(Class<F> fishClass) {
return FACTORY_STRATEGY_MAP.get(fishClass);
}
public static String[] names() {
return NAMES;
}
}
This enum could then be used in the following manner -
Fish fish = FishType.valueOf("BLUE_FISH").create(0, 0);
or
Fish fish = FishType.RED_FISH.create(0, 0);
or, if you need to know the type of the created fish, you can use this call -
BlueFish fish = FishType.getFactory(BlueFish.class).createFish(0, 0);
To populate the items in a menu or obtain all fish types for any other reason, you can use the names() method -
String[] names = FishType.names();
To add new types, the only code that needs to be edited is to add a new enum declaration such as
GREEN_FISH(GreenFish.class, new FactoryStrategy<GreenFish>(){
public GreenFish createFish(int x, int y) {
return new GreenFish(x, y);
}}),
It may seem like a lot of code, but it's already been written, it provides a clean API to call from other code, it provides pretty good type-safety, allows the fish implementations the flexibility to have whatever constructors or builders that they want, it should be fast performing, and it doesn't require you to pass around arbitrary string values.
If you are just really into keeping it concise, you could also use a template method in the enums -
public enum FishType {
BLUE_FISH(){
public BlueFish create(int x, int y) {
return new BlueFish(x, y);
}
},
RED_FISH(){
public RedFish create(int x, int y) {
return new RedFish();
}
};
public abstract <F extends Fish> F create(int x, int y);
}
With this, you still get a lot of the same functionality such as
Fish fish = FishType.valueOf("BLUE_FISH").create(0, 0);
and
Fish fish = FishType.RED_FISH.create(0, 0);
and even
RedFish fish = FishType.RED_FISH.create(0, 0);
Study the Factory Design Pattern. That is essentially what you are doing here, but will be a little bit cleaner if you use it explicitly.
It is not always just a giant switch statement. For instance, you may have a table of dynamically loaded assemblies and/or types, each of which have a function called "GetTypeName" and another function called "CreateInstance". You would pass a string to a factory object, which would look in the table for that typename and return the result of the CreateInstance function on that factory object.
No, this isn't reflection, people were doing this long before Java came along. This is how COM works for example.
Reflection seems to be the best solution for this issue and I am glad to have this technique in my toolbox. Here is the code that worked:
public void addFish(String s, int qt){
try{
Class<?> theClass = Class.forName("ftank." + s);
Class[] ctorArgs = {ftank.FishTank.class};
Constructor ctor = theClass.getDeclaredConstructor(ctorArgs);
for(int i=0;i<qt;i++){stock.add((Fish)ctor.newInstance(this));}
} catch (ClassNotFoundException e) {...
I had to include the package name as part of the class string. I also had to make the constructors public. I was unable to implement this solution with int arguments in the constructors but I managed to find a way around using them which was cleaner anyways. The only problem now is that I must update the array of Strings used in the JComboBox everytime
I add a new species of Fish. If anyone knows a way of having java generate a list of the names of all the classes in a package which inherit from a given base class that would be helpful. Your suggestions so far were very helpful and I am greatful.
Related
I am trying to make a simulation that simulates simple creatures and carnivorous creatures.
I have a class called creature and a subclass called carnCreature. I have a method in creature called eat, that takes in a one type of object, but I need the eat method in the carnCreature class to take in a list of creatures. I tried naming the method the same as it is named in the creature class, but when I try to call it, java doesn't accept the updated parameters.
package simulationObjects;
import java.awt.Color;
import java.util.List;
import java.util.Random;
import java.lang.Math.*;
public class Creature {
public int x;
public int y;
public int maxTilesX;
public int maxTilesY;
public Color color;
public float health = 50;
public int life = 0;
public Creature (int x, int y, Color color, int maxTilesX, int maxTilesY) {
this.x = x;
this.y = y;
this.color = color;
this.maxTilesX = maxTilesX;
this.maxTilesY = maxTilesY;
}
public void update(Tile tile) {
eat(tile);
life++;
health-=1;
}
public void eat(Tile currentTile) {
if (currentTile.color == this.color) {
health += 3;
currentTile.color = Color.GRAY;
}
}
public boolean isCarnivore() {
return false;
}
}
package simulationObjects;
import java.awt.Color;
import java.util.List;
public class CarnCreature extends Creature{
private static final boolean CANABOLIC = false;
public CarnCreature(int x, int y, Color color, int maxTilesX, int maxTilesY) {
super(x, y, color, maxTilesX, maxTilesY);
// TODO Auto-generated constructor stub
}
public void update(List<Creature> creatures) {
eat(creatures);
life++;
health-=1;
}
public void eat(List<Creature> creatures) {
for (Creature creature : creatures) {
if (CANABOLIC) {
if (creature.color == this.color) {
health += 3;
creature.health = 0;
}
} else {
if (creature.color == this.color && creature.isCarnivore() == false) {
health += 3;
creature.health = 0;
}
}
}
}
public boolean isCarnivore() {
return true;
}
}
The eat function is being called later like this:
for (Creature creature : creatures) {
if (creature.isCarnivore()) {
creature.upadte(creatures);
} else {
creature.update(tiles.get(creature.x).get(creature.y));
}
}
I am trying to store the creatures and the carnCreatures in the same list, "creatures." Is this the problem, and do I need to store them in separate lists?
Thanks
You have a two options:
Once you know if the creature is carnivore cast it and access the method
Create a method with the same "signature", that is, same name AND arguments.
The second option is the more elegant. Using the "magic" of polymorphism each class will have its method called and you won't need to check the class with the isCarnivore() method. But you will need to get the list of creatures from the tile.
The isCarnivore() test will not spare you to cast to the subclass type as you manipulate as declared type the Creature the base class :
for (Creature creature : creatures) {
if (creature.isCarnivore()) {
((CarnCreature)creature).update(creatures);
} else {
creature.update(tiles.get(creature.x).get(creature.y));
}
}
So the isCarnivore() appear helpless as if (instanceof CarnCreature) would have the same effect and consequences.
Is this the problem, and do I need to store them in separate lists?
It would be better as you don't want manipulate them in an uniform way.
Using the base class to group them in a unique List make your task harder.
But in fact you have a deeper issue. Here eat() is not a overrided method but an overloaded method in the subclass. Same thing for update().
It means that in both cases the two methods are defined in the subclass.
Such a design will not allow to benefit from a polymorphism feature because you want to invoke the first method on the base class instance and invoke the overloaded method on the subclass instance.
In terms of concept, a carnivore creature IS not a creature. Their type of behavior is very different : one consumes a thing (a tile) and the other consumes a very different thing (a list of creature).
To benefit from polymorphism you should re-design the base class and the subclass to override the methods and not overload them. But as you pass really different types in the parameters, you are stuck.
So in your case I think that I would not even create a inheritancy relation between theses classes.
suppose in Eclipse I have three packages with the following classes in each:
Packages: Classes
Head: head.java
Body: arms.java
Legs: feet.java
I want to define class info in brain.java and pass it through methods to the other classes (arms.java and feet.java) and update the contents of info.
class info {
// some vars such as bools,ints,strings
}
For example, have updateArms be a method defined in arms.java. I want to do the following in brain.java:
arms.updateArms( info );
I am having trouble finding how to first define a class that behaves this way, and secondly how to pass it as a parameter to another linked class.
First, you should learn about Java naming convention.
For example, package should be head, and the class should be Head.
Go back to your design: In OOP, we see the program as interactions between object instances.
In your example, it may look like:
class Arm {
void moveUp(SomeInfo info) {
...
}
}
class Brain {
private Arm leftArm;
private Arm rightArm;
void reachForward() {
rightArm.moveUp(...);
}
void connectLeftArm(Arm arm) {
this.leftArm = arm;
}
//....
}
class Body {
Brain brain;
Arm leftArm;
Arm rightArm;
public Body() {
this.brain = new Brain();
this.leftArm = new Arm();
this.rightArm = new Arm();
this.brain.connectLeftArm(this.leftArm);
this.brain.connectRightArm(this.rightArm);
}
}
I wish this demonstrate the difference of way of thinking.
If you start get used to the way OOP see things, then you can take next step in refining your design (e.g. by different design pattern)
You can achieve this using Inheritance.
Inheritance can be defined as the process where one class acquires the properties (methods and fields) of another. With the use of inheritance the information is made manageable in a hierarchical order. The class which inherits the properties of other is known as subclass (derived class, child class) and the class whose properties are inherited is known as superclass (base class, parent class).
Sample Code helps you how to use the methods and properties of other classes.
class Calculation {
int z;
public void addition(int x, int y) {
z = x + y;
System.out.println("The sum of the given numbers:"+z);
}
public void Subtraction(int x, int y) {
z = x - y;
System.out.println("The difference between the given numbers:"+z);
}
}
public class My_Calculation extends Calculation {
public void multiplication(int x, int y) {
z = x * y;
System.out.println("The product of the given numbers:"+z);
}
public static void main(String args[]) {
int a = 20, b = 10;
My_Calculation demo = new My_Calculation();
demo.addition(a, b);
demo.Subtraction(a, b);
demo.multiplication(a, b);
}
}
I'm programming a game as Java applet, and I have two different classes with similar variables - Pavement and Way.
I would like to create an Array that will contain objects from both classes. I have to change for example x and y of all these objects so I would like to create an Array. Is this possible to do?
I have tried ArrayList<Object> obj = new ArrayList<Object>(); but if I obj.add(0, theWay); I can't find a way to read variables.
I also tried System.out.print(obj.get(0)); and got rpg.way#2c61bbb7.
In short, you would like to call a method (like setX()) on a Way instance, or on a Pavement instance, without knowing if the object is a Way or a Pavement.
This is exactly the problem that polymorphism solves. Define an interface Locatable, and make your two classes implement this interface. Then create a List<Locatable>, and you'll be able to add Ways and Pavements inside it:
public interface Locatable {
public void setX(int x);
public void setY(int y);
public int getX();
public int getY();
}
public class Way implements Locatable {
...
}
public class Pavement implements Locatable {
...
}
List<Locatable> locatables = new ArrayList<Locatable>();
list.add(new Way());
list.add(new Pavement());
for (Locatable locatable: locatables) {
locatable.setX(22);
locatable.setY(43);
}
for (Locatable locatable: locatables) {
System.out.println("the locatable is an instance of " + locatable.getClass());
System.out.println("its location is " + locatable.getX() + ", " + locatable.getY());
}
You can use ArrayList<Object> and you check the type of the objects.
For instance:
ArrayList<Object> L = new ArrayList<Object>();
//..add objects
for (Object o : L){
if(o.getClass() == Class1.class){
Class1 obj1 = (Class1) o;
//...
}else if(o.getClass() == Class2.class){
Class2 obj2 = (Class2) o;
//...
}else{
//...
}
}
You can also use instanceof to check the type.
You mention the classes are different but similar? If the classes are similar enough that you want to try calling the same methods on the classes, you may want to consider generalizing the classes using an abstract class: This is much better practice than the "instance of" and caste solution. You could also use an interface, but the abstract class allows you to actually implement changeX() and changeY() if they work the same way. For example, if you want to call changeX(int x) or changeY(int y) on either of the objects regardless of whether they are Way or Pavement objects, you probably want to do something like this:
public abstract class Changeable
{
public void changeX(int x) {
this.x = x;
}
public void changeY(int y) {
this.y = y;
}
protected int x;
protected int y;
}
public Class Pavement extends Changeable{ ... }
public Class Way extends Changeable{ ... }
At which point you can create an array of the interface and insert objects inside like so:
List<Changeable> paths = new ArrayList<Changeable>();
paths.add(new Way());
paths.add(new Pavement());
Changeable path = paths.get(0);
path.changeX(5);
path.changeY(7);
I have a JAVA class with lots of fields. They should basically be set at the constructor phase and never change. Semantically the class then is an immutable one.
public class A{
final int a;
final short b;
final double e;
final String f;
final String g;
//and more
}
The problem is that normally these fields have default values and therefore I do not want to always burden the user with a constructor with all of them. Most time, they just need to set a couple of them. There are a couple of ways to solve this:
I would need lots of constructor with different signature.
Create a bunch of set method of these field and only set those non-default value. But this somehow indicate a different semantics other than immutable nature.
Create a new parameter class that is mutable and use that class as constructor.
None of that is totally satisfactory. Is there any other approach? Thanks.
One way
I would use a combination of a parameter class and a fluent builder API for creating the parameter:
public class A {
private final int a;
private final short b;
private final double e;
private final String g;
public static class Aparam {
private int a = 1;
private short b = 2;
private double e = 3.141593;
private String g = "NONE";
public Aparam a(int a) {
this.a = a;
return this;
}
public Aparam b(short b) {
this.b = b;
return this;
}
public Aparam e(double e) {
this.e = e;
return this;
}
public Aparam g(String g) {
this.g = g;
return this;
}
public A build() {
return new A(this);
}
}
public static Aparam a(int a) {
return new Aparam().a(a);
}
public static Aparam b(short b) {
return new Aparam().b(b);
}
public static Aparam e(double e) {
return new Aparam().e(e);
}
public static Aparam g(String g) {
return new Aparam().g(g);
}
public static A build() {
return new Aparam().build();
}
private A(Aparam p) {
this.a = p.a;
this.b = p.b;
this.e = p.e;
this.g = p.g;
}
#Override public String toString() {
return "{a=" + a + ",b=" + b + ",e=" + e + ",g=" + g + "}";
}
}
Then create instances of A like this:
A a1 = A.build();
A a2 = A.a(7).e(17.5).build();
A a3 = A.b((short)42).e(2.218282).g("fluent").build();
Class A is immutable, the parameters are optional, and the interface is fluent.
Two things you can do:
Many constructor overloads
Use a builder object
This is only a semi-serious suggestion, but we can modify mikera's answer to be typesafe.
Say we have:
public class A {
private final String foo;
private final int bar;
private final Date baz;
}
Then we write:
public abstract class AProperty<T> {
public static final AProperty<String> FOO = new AProperty<String>(String.class) {};
public static final AProperty<Integer> BAR = new AProperty<Integer>(Integer.class) {};
public static final AProperty<Date> BAZ = new AProperty<Date>(Date.class) {};
public final Class<T> propertyClass;
private AProperty(Class<T> propertyClass) {
this.propertyClass = propertyClass;
}
}
And:
public class APropertyMap {
private final Map<AProperty<?>, Object> properties = new HashMap<AProperty<?>, Object>();
public <T> void put(AProperty<T> property, T value) {
properties.put(property, value);
}
public <T> T get(AProperty<T> property) {
return property.propertyClass.cast(properties.get(property));
}
}
Aficionados of advanced design patterns and/or obscure Java tricks will recognise this as a typesafe heterogeneous container. Just be grateful i didn't use getGenericSuperclass() as well.
Then, back in the target class:
public A(APropertyMap properties) {
foo = properties.get(AProperty.FOO);
bar = properties.get(AProperty.BAR);
baz = properties.get(AProperty.BAZ);
}
This is all used like this:
APropertyMap properties = new APropertyMap();
properties.put(AProperty.FOO, "skidoo");
properties.put(AProperty.BAR, 23);
A a = new A(properties);
Just for the lulz, we can even give the map a fluent interface:
public <T> APropertyMap with(AProperty<T> property, T value) {
put(property, value);
return this;
}
Which lets callers write:
A a = new A(new APropertyMap()
.with(AProperty.FOO, "skidoo")
.with(AProperty.BAR, 23));
There are lots of little improvements you could make to this. The types in AProperty could be handled more elegantly. APropertyMap could have a static factory instead of a constructor, allowing a more fluent style of code, if you're into that sort of thing. APropertyMap could grow a build method which calls A's constructor, essentially turning it into a builder.
You can also make some of these objects rather more generic. AProperty and APropertyMap could have generic base classes which did the functional bits, with very simple A-specific subclasses.
If you're feeling particularly enterprise, and your domain objects were JPA2 entities, then you could use the metamodel attributes as the property objects. This leaves the map/builder doing a bit more work, but it's still pretty simple; i have a generic builder working in 45 lines, with a subclass per entity containing a single one-line method.
One interesting option is to create a constructor that takes a Map<String,Object> as input which contains the values that the user wants to specify.
The constructor can use the value provided in the map if present, or a default value otherwise.
EDIT:
I think the random downvoters have completely missed the point - this isn't always going to be the best choice but it is a useful technique that has several advantages:
It is concise and avoids the need to create separate constructors / builder classes
It allows easy programmatic construction of parameter sets (e.g. if you are constructing objects from a parsed DSL)
This is a technique that is frequently used and proven to work in dynamic languages. You just need to write decent tests (which you should be doing anyway!)
Having many fields could be an indication that one class does too much.
Maybe you can split the class up in several immutable classes and pass instances of these classes to the constructors of the other classes. This would limit the number of constructors.
I am trying to do something I would not normally do, it is a bit odd, but I'd like to make it work. Essentially I have a factory that has to create objects by calling the constructor with different types of data (A and B take different types in the code below). I seem to have gotten my self stuck going down the generics route (I do need the code to be as compile time typesafe as possible). I am not opposed to writing the code differently (I'd like to keep the idea of the factory if possible, and I do not want to have to add in casts - so the "data" parameter cannot be an "Object").
Any thoughts on how to fix the code with generics or an alternative way of doing it that meets my requirements?
(Technically this is homework, but I am the instructor trying out something new... so it isn't really homework :-)
public class Main2
{
public static void main(String[] args)
{
X<?> x;
x = XFactory.makeX(0, "Hello");
x.foo();
x = XFactory.makeX(1, Integer.valueOf(42));
x.foo();
}
}
class XFactory
{
public static <T> X<T> makeX(final int i,
final T data)
{
final X<T> x;
if(i == 0)
{
// compiler error: cannot find symbol constructor A(T)
x = new A(data);
}
else
{
// compiler error: cannot find symbol constructor B(T)
x = new B(data);
}
return (x);
}
}
interface X<T>
{
void foo();
}
class A
implements X<String>
{
A(final String s)
{
}
public void foo()
{
System.out.println("A.foo");
}
}
class B
implements X<Integer>
{
B(final Integer i)
{
}
public void foo()
{
System.out.println("B.foo");
}
}
I don't see a way to make it work. I don't really think it should work either. When calling your makeX() function the calling code needs to know what integer parameter corresponds to what type of data to pass in. IOW, your abstraction is very leaky in the first place, and what you're really implementing is a rudimentary form of polymorphism, which you might as well use method overloading for, i.e.:
X makeX(String data) {
return new A(data);
}
X makeX(Integer data) {
return new B(data);
}
Of course it's a toy problem and all that. One way to make it work would be to make the client aware of implementation classes and add a Class<T> argument that you instantiate through reflection. But I suppose that would be kind of defeating the purpose.
I don't think what you're trying to do is possible without casting.
With casting, you have two options
if(i == 0)
{
x = new A((Integer)data);
}
else
{
x = new B((String)data);
}
}
or
class A
implements X<String>
{
A(final Object s)
{
}
}
...
class B
implements X<Integer>
{
B(final Object i)
{
}
}
Probably the closest thing you could get whilst retaining static type safety and having lazy construction is:
public static void main(String[] args) {
X<?> x;
x = aFactory("Hello").makeX();
x.foo();
x = bFactory(42).makeX();
x.foo();
}
private static XFactory aFactory(final String value) {
return new XFactory() { public X<?> makeX() {
return new A(value);
}};
}
public static XFactory bFactory(final Integer value) {
return new XFactory() { public X<?> makeX() {
return new B(value);
}};
}
interface XFactory() {
X<?> makeX();
}
So we create an instance of an abstract factory that creates the appropriate instance with the appropriate argument. As a factory, the product is only constructed on demand.
Clearly something had to give. What would you expect XFactory.makeX(1, "Hello") to do?
This is not possible without casting. As I have said elsewhere - generics don't remove the need for casting, but they mean that you can do all the casting in one place.
In the setup you describe, the factory method is exactly where all the under-the-hood work takes place. It's the spot where your code tells the compiler "I know you don't know what these types are, but I do, so relax.
It's entirely legit for your factory method to know that if i==1, then the data must be be of type Integer, and to check/enforce this with casting.