I am writing this query service that's suppose to work with couple predefined classes. However, it seems to me to be redundant that, when using this service class, I need to pass both the Type of the class and a class object it self. For example, to query "Contact" object, I'll need to provide both and Contact.class, like the following:
lookupService = new MyLookupServiceImpl<Contact>(Contact.class);
In this case, is there a way to initialize Class without passing in "Contact.class"?
The service class look like the following:
public class MyLookupServiceImpl<T> {
private Class<T> cls;
public MyLookupServiceImpl(Class<T> clz){
this.cls = clz;
}
public T queryObject(String sql) {
try {
QueryResult result = ConnectionFactory.getConnection().query(sql);
if(result.getSize()>0){
T obj=null;
MyObject sObj = result.getRecords()[0];
MyObjectBuilder<T> ob = new MyObjectBuilder<T>(cls);
obj = ob.buildORMObject(sObj);
return obj;
}
} catch (ConnectionException e) {
logger.warn(e.toString());
}
return null;
}
}
Any suggestion would be appreciated.
Thank you,
The answer is NO. In java, because of type erasure, there is no other way infer/get this info at runtime other that passing in the type info as a Class<?> instance.
This is most likely necessary because it is not possible to create a new instance of an arbitrary class indicated by a type parameter; in other words, you cannot do:
T obj = new T();
because of the way Java generics are implemented (with type erasure).
Note that in your code, cls is passed to MyObjectBuilder<T> which is most likely an object that creates new instances of T. MyObjectBuilder<T> uses reflection to create a new instance of T, with a statement like this:
T obj = cls.newInstance();
See also: Create new instance of T in Java
Bala is correct (+1) unfortunately. However, if you use a Dependency Injection framework like Guice you could ask Guice to pass you a TypeLiteral which is the type you're interested in. You could do something like this:
public class MyLookupServiceImpl<T> {
private Class<T> cls;
#Inject
public MyLookupServiceImpl(TypeLiteral<T> type){
this.cls = (Class<T>)type.getRawType();
}
}
Then use the Guice Injector to pass you instances of MyLookupServiceImpl. Just an idea! :)
Related
Is it okay to pass an object type, or simply a type of any kind into the constructor of a new object then not use it inside of the constructor?
The purpose is to have the new object be created based on what parent class it resides in (it is aggregated) so it generates different variables for itself based on that fact.
Or is there a better way to do this?
public class ObjectA {
private MalleableObject obj;
public void createObject(){
obj = new MalleableObject(this);
}
}
public class ObjectB {
private MalleableObject obj;
public void createObject(){
obj = new MalleableObject(this);
}
}
public class MalleableObject{
private boolean doIBelongToA;
public MalleableObject(ObjectB obj){
doIBelongToA = false;
}
public MalleableObject(ObjectA obj){
doIBelongToA = true;
}
}
The approach you are taking will definitely work, But the question is whether it is a good idea or not, The answer is depends on the use case that you are trying to tackle.
You asked about taking a object as parameter in constructor and not using it
If you are not using the property of passed parameter then why to take that in parameter, for that we have empty constructor , even when you don't specify it is by default injected
public MalleableObject(ObjectB obj){
doIBelongToA = false; // if you are directly setting the value without
//using obj b then use default constructor.
}
public MalleableObject(ObjectA obj){
doIBelongToA = true;
}
/*If you are interested in setting the value based on the reference only there
is no problem with your approach as well, One alternative you can take to
combine both of them in single constructor and check the reference and set
the value accordingly*/
If you use the property of the object for creating new object then certainly it's a good idea,
Copy constructor if you are using same object as parameter, Prototype design pattern if you are creating your object with similiar object already created
The example that you gave is a very basic example which does not highlight any use case and hence the approach you have taken will not be suggested
The simpler approach would be just adding the boolean variable in the constructor of MalleableObject.
public class ObjectA {
private MalleableObject obj;
public void createObject(){
obj = new MalleableObject(true);
}
}
public class ObjectB {
private MalleableObject obj;
public void createObject(){
obj = new MalleableObject(false);
}
}
public class MalleableObject{
private boolean doIBelongToA;
public MalleableObject(boolean doIBelongToA){
this.doIBelongToA = doIBelongToA;
}
}
This would easily assure that the object made from class A has value true for doIBelongToA.
Also, you will not have to add different constructors for further classes, if added, ensuring extensibility
While you aren't using it directly, you are definitely using information from the parameter. Personally, I'd save the reference, or at least its type, for later use in case you need it and implement doIBelongToA as a method, but there's nothing technically wrong with your approach:
public class MalleableObject{
private Class ownerType;
public MalleableObject(Class ownerType) {
this.ownerType = ownerType;
}
public boolean doBelongToA() {
return ownerType.equals(ObjectA.class);
}
}
The above approach works but it's not a best practice and hence has some limitations. I would suggest you use builder pattern where you can create use of a builder to create an object and then have a method that defines the behavior. You will be able to extend this to add variables/business logic in the future.
I think the Factory pattern is also good
thing to look at
I have code like so:
public class Something<T extends Thing> {
private T theThing;
public Something(T aThing) {
theThing = aThing;
}
public T getTheThing() {
return theThing;
}
}
Then, somewhere, I write this:
Something something = new Something<SpecialThing>(new SpecialThing()); // SpecialThing extends Thing
SpecialThing specialThing = something.getTheThing(); // Error: getTheThing() returns object of type Thing instead of SpecialThing!
Why do I not get theThing as a SpecialThing, but as a Thing instead?
Thing class:
public abstract class Thing {
}
SpecialThing class:
public class SpecialThing extends Thing {
}
The reason is that you are using a raw type reference to your Something. That means that the only thing Java can say for sure is that the type returned extends Thing, so that is the type you are getting.
Add the type parameter like this to return a SpecialThing:
Something<SpecialThing> something = new Something<SpecialThing>(new SpecialThing());
SpecialThing specialThing = something.getTheThing();
Because you didn't specify the type in the variable declaration. You might have instantiated it with SpecialThing, but that's not how you remember it.
You'll notice it works when you use this instead.
Something<SpecialThing> something = new Something<SpecialThing>(new SpecialThing());
It's the same as defining ArrayList t = new ArrayList<String>();. This will still be regarded as ArrayList<Object>, not ArrayList<String>.
JavaDocs on raw types.
I'd like to create a map of singleton classes that I can access via a cross reference in order to respond to a specific request. I have the following implemented, but having trouble getting to an actual reference that I can call getInstance() on.
Map<Integer, Class<? extends Thing>> xref = new HashMap<Integer, Class<? extends Thing>>();
xref.put(1, ThingOne.class);
xref.put(2, ThingTwo.class);
Class<? extends Thing> t = xref.get(1);
Ultimately then do something like...
something.perform(arg1, arg2);
Can't figure out how to get from "t" to "something", or if that's possible given the way I have it coded. I tried calling .cast(Thing.class).getInstance(), but got a Cast exception. Also tried reflection to get the getInstance() method, but no luck there either.
It may be I'm going down the wrong path altogether. Given 1..n possible functions, any given instance of the solution may only require a subset of these. In addition, I'd like to easily add/delete classes and manage the interface through config vs. a bunch of object instantiations at startup time.
Thanks!!!
I don't quite understand your purpose in creating this map. From what you've written, it seems you could simply put static getInstance() methods, that return singletons, on each relevant class. Or even more trivial: put each shared instance as a static final field of its class.
If you must use a map, don't use an integer as a key. The class is the key, and its instance is the value. Something like:
private static final Map<Class<?>,Object> singletons = new HashMap<>();
public static synchronized <T> T getSingleton(Class<T> klass) {
Object obj = singletons.get(klass);
if (obj == null) {
try {
obj = klass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
singletons.put(klass, obj);
}
return klass.cast(obj);
}
The creation code there is icky and requires a public no-arg constructor. You could alternatively call via reflection a static, specifically named method on each class to create the desired instance ("createInstance"), which might be a bit more flexible, but when you do that, it once again asks why bother with the map, when you could call a method on the class directly?
One interesting possibility with the map is to supply functions to create instances. In Java 8 syntax (import java.util.function.Supplier):
private static final Map<Class<?>,Object> singletons = new HashMap<>();
public static synchronized <T> T getSingleton(Class<T> klass) {
Object obj = singletons.get(klass);
if (obj instanceof Supplier) {
obj = ((Supplier<?>)obj).get();
singletons.put(klass, obj);
}
return klass.cast(obj);
}
public static synchronized <T> void declareSingleton(Class<T> klass, Supplier<T> supplier) {
if (Supplier.class.isAssignableFrom(klass)) {
// prevent Supplier<Supplier<?>> weirdness;
// could use separate maps if those are really needed
throw new UnsupportedOperationException();
}
singletons.put(klass, supplier);
}
static {
// add creation expressions for several classes;
// instances will not be created until needed
declareSingleton(ThingOne.class, () -> new ThingOne());
declareSingleton(ThingTwo.class, () -> new ThingTwo(123));
}
I'm not sure if this is what you want but it might contain some ideas.
Edit: I've just realized a problem of using the Class itself as a key: it causes the class to be loaded even if it is not needed during a particular program run. Using a String key would avoid loading unneeded classes, but increases fragility. This is another argument against using a map for all this.
I have a database table that contains a column named type. For every row in my database column I have to create an object depending on the type. At the moment I use if else statements for that:
if (type.equals("object1")){
Object1 object1 = new Object1();
}
else if (type.equals("object2")){
Object2 object2 = new Object2();
}
Somewhat nicer would be to use an enum, as the number of types is limited but is there a possibility to let the creation of an object depend on the value of the String?
I'm open to suggestions that might solve my problem in another way than I am trying to.
You could create a map from String to Class and use newInstance. That however relies on the existence of no-arg constructors.
import java.util.*;
class Test {
public static void main(String[] args) throws Exception {
Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
classes.put("object1", Object1.class);
classes.put("object2", Object2.class);
String type = "object2";
Object obj = classes.get(type).newInstance();
//...
}
}
class Object1 { ... }
class Object2 { ... }
You can use
Object o = Class.forName(type).newInstance();
If you have an int and two Strings as arguments you need.
Object o = Class.forName(type)
.getConstructor(int.class, String.class, String.class)
.newInstance(intValue, string1, string2);
Another possibility is to use factory methods
Object o = getClass().getMethod("create_" + type).invoke(null);
static Object1 create_object1() {
return new Object1(/* with args */);
}
static Object2 create_object2() {
return new Object2(/* with other args */);
}
but the most flexible approach may be to use a switch
Object o;
switch(type) { // in Java 7
case "object1": o = new Object1(); break;
case "object2": o = new Object2(); break;
What would be more elegant is using closures in Java 8.
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
package main;
import java.util.ArrayList;
import java.util.List;
public class TempClass {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
List<Class> classes = new ArrayList<Class>() {{
add(Class.forName("main.Object1"));
add(Class.forName("main.Object2"));
}};
for (Class aClass : classes) {
Object o = aClass.newInstance();
}
}
}
package main;
public class Object1 {
public Object1() {
System.out.println("Object1");
}
}
package main;
public class Object2 {
public Object2() {
System.out.println("Object2");
}
}
If the classes in question are part of an inheritance hierarchy and you happen to use some kind of mapping tool/framework like Hibernate or iBATIS/MyBatis, it might include a way to configure so called discriminators.
You could then configure your "type" column as the descriminator column and map the supported values to your classes. The framework will then instantiate the appropriate classes for you when you retrieve the objects from the database.
Using switch or if statements forces you to hard-code the various options, so you will obtain a not so flexible solution, in my opinion.
I always prefer to write code that is extensible for future software developments.
So I suggest you to use Object o = Class.forName(type).newInstance(); putting in your database the name of the class you want to instantiate and, if you need it, a custom label.
EDIT
If you have to pass some parameter to the constructors of the classes you have to instantiate, is convenient to use Constructor.newInstance() instead of Class.newInstance(); and use the Constructor object to instantiate the class, for example:
Class myClass = Class.forName(type);
// get the constructor you need specifying parameters type
Constructor constructor = myClass.getConstructor(String.class);
Object myObject = constructor.newInstance("constructor-arg");
Is there a (better) way to dynamically create Objects?
Right now I'm using a simple 'factory pattern' solution as following:
String classType = generalObject.getClass().toString();
if(classType.equals("class be.testApp.UserObject")) {
return UserObject.fromByteArray(data);
//return new UserObject();
}
else if(classType.equals("class.be.testApp.NewsObject")) {
return NewsObject.fromByteArray(data);
//return new NewsObject();
}
This code is not a factory pattern and no object is created. You evaluate the class name and call a static method on a class.
Now it looks like you have an object (generalObject) and want to create a new instance of the very same type. If all possible types have a public default constructor (convention!), then you can use this to create a new instance based on the given object:
Object newObject = generalObject.getClass().newInstance();
(but maybe I still didn't get your idea...)
You could use reflection here, something like
final Class<?> clazz = generalObject.getClass();
final Method method = clazz.getMethod("fromByteArray", data.getClass());
return method.invoke(null, data);
should do.
String classType = generalObject.getClass().toString();
if(classType.equals("class be.testApp.UserObject")) {
return UserObject.fromByteArray(data);
}else if(classType.equals("class.be.testApp.NewsObject")) {
return NewsObject.fromByteArray(data);
}
This is very complicated. Since apparently both classes are on your compile classpath, just use the class objects, not their string representations:
Class<?> classType = generalObject.getClass();
if(UserObject.class.equals(classType)) {
return UserObject.fromByteArray(data);
}else if(NewsObject.class.equals(classType)) {
return NewsObject.fromByteArray(data);
}