I am trying to create a collecion of objects using reflection. I have the class name. But at compile time have no idea about the constructor. I used this tutorial.
import java.lang.reflect.*;
public class constructor2 {
public constructor2()
{
}
public constructor2(int a, int b)
{
System.out.println(
"a = " + a + " b = " + b);
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct
= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
Now let's say I don't know abut the parameters of the constructors. (Whether it's a default constructor or two integers). The costructor parameters can change from class to class. I have no idea about the Constructor2 class. But I have the className.
So I cant create the "argList" as above. Any idea how I can create a object from constructors then.
I did as follows
Constructor[] brickConstructorsArray = brickClass.getConstructors();
for (Constructor constructor : brickConstructorsArray) {
Class[] constructorParams = constructor.getParameterTypes();
Object argList[] = new Object[constructorParams.length];
int index = 0;
for (Class cls : constructorParams) {
if (cls.equals(Sprite.class)) {
Object spriteOb = foundSprite;
argList[index] = spriteOb;
} else if (cls.equals(double.class)) {
Object dblObj = new Double(0.0);
argList[index] = dblObj;
}
index++;
}
brickObject = (Brick) constructor.newInstance(argList);
}
This would work for costrcutors which will have Sprite or double parameters. To include other possibilities I'd have to create a loooong if else chain. Need help.
Do you really not care what values you're going to pass? You could have a map from each primitive class to a default value of its wrapper type:
// You can make this a static member somewhere
Map<Class<?>, Object> primitiveValues = new HashMap<Class<?>, Object>();
primitiveValues.put(char.class, '\0');
// etc
... and just use null for any non-primitive type. So:
Constructor[] brickConstructorsArray = brickClass.getConstructors();
for (Constructor constructor : brickConstructorsArray) {
Class[] constructorParams = constructor.getParameterTypes();
Object argList[] = new Object[constructorParams.length];
// When you need the index, there's no point in using the enhanced
// for loop.
for (int i = 0; i < constructorParams.length; i++) {
// Fetching the value for any non-primitive type will
// give null, which is what we were going to use anyway.
argList[i] = primitiveValues.get(constructorParams[i]);
}
brickObject = (Brick) constructor.newInstance(argList);
}
Of course I'd expect this to fail quite often. After all, if you don't know anything about the constructor you're calling, you have no idea what arguments are going to be valid.
Is this really something you're expecting to find useful?
Related
I hava a POJO class which has a lot of getters. Now I’m using the class Method from the java.lang.reflect package to get all the get methods from that class. I want to invoke those getters but I don’t have idea how. arraylistFilter is an Arraylist<pojo> that contains the result from my request. Option is an Object Field data type which is the one that actually has like 100 getters. How can I get each of them without needed to call 1 by 1. what goes in my .??? there is where I want to be able to invoke my getters.
try {
Class<? extends Options> testObject = new Options().getClass();
Method[] methods = testObject.getMethods();
for (Method method : methods) {
String name = method.getName();
if (method.getName().startsWith("get") &&
method.getGenericParameterTypes().length == 0) {
for (int i = 0; i < arrayListFilter.size(); i++) {
arrayListFilter.get(i).getOptions().???;
}
}
}
} catch (Exception e) {
// do something with the exceptions
}
It will be something like this:
try {
Method[] methods = Options.class.getMethods();
for (Method method : methods) {
String name = method.getName();
if (method.getName().startsWith("get") &&
method.getParameterTypes().length == 0) {
for (int i = 0; i < arrayListFilter.size(); i++) {
Object value = method.invoke(arrayListFilter.get(i).getOptions());
// The actual type of 'value' will depend on the getter's
// formal return type AND the actual type it returns.
// When the return type is a primitive, it is mapped to
// the corresponding wrapper.
}
}
}
} catch (Exception e) {
// do something with the exceptions
}
We have simplified / fixed the code that gets the Method objects, and have changed getGenericParameterTypes() to getParameterTypes(). Generic type parameters are not relevant here, but you do need to filter out getters that require arguments, because there is no way to supply sensible argument values.
I encountered a problem while dealing with a call of a static method in another class.
So I have a function which is like follow, where I need to extract two values, a counter and an object :
public static int getEarliestValue(Map<DBObject, DBCursor> cursorMap, DBObject result) {
int mergeCount = 1;
if (!cursorMap.isEmpty()) {
long ealiest = Long.MAX_VALUE;
for (DBObject o : cursorMap.keySet()) {
// do stuff to init tmp
...
if (tmp < ealiest) {
result = o;
ealiest = tmp;
}
// other stuff .....
}
return mergeCount;
}
here is how I call it in my other class :
DBObject result= null;
int mergeCount = MongoTickReaderUtil.getEarliestValue(cursorList, result);
I checked in debug mod and result is set in getEarliestValue but when it's go out of the function call result is still null.
I thought that references where like pointers with the difference that we couldn't do arithmetic operation on the reference itself, but with this behavior it seems that even if we change the pointed value it still only in the local scope.
The only idea that I found was to put it in a List but this is neither elegant nor optimal.
Any suggestion ? Thanks in advance.
Passing result here is the same thing as passing null. You have to return this DBObject someway. For example, you can return something like Map.Entry<Integer, DBObject>. This could not be supposed to be a good solution, maybe you should create some class like BlahBlahResult, containing both int result and DBObject.
If you really-really want to pass it as a parameter, you may pass something like AtomicReference<DBObject> and set it inside the method:
public static int getEarliestValue(Map<DBObject, DBCursor> cursorMap, AtomicReference<DBObject> result) {
int mergeCount = 1;
if (!cursorMap.isEmpty()) {
long ealiest = Long.MAX_VALUE;
for (DBObject o : cursorMap.keySet()) {
// do stuff to init tmp
...
if (tmp < ealiest) {
result.set(o);
ealiest = tmp;
}
// other stuff .....
}
return mergeCount;
}
...
...
AtomicReference<DBObject> resultReference = new AtomicReference<>(null);
int mergeCount = MongoTickReaderUtil.getEarliestValue(cursorList, resultReference);
DBObject result = resultReference.get();
In java "References to Objects are passed by value".
DBObject result= null;
int mergeCount = MongoTickReaderUtil.getEarliestValue(cursorList, result); // here result --> null i.e, points to nothing
public static int getEarliestValue(Map<DBObject, DBCursor> cursorMap, DBObject result) {
int mergeCount = 1;
if (!cursorMap.isEmpty()) {
long ealiest = Long.MAX_VALUE;
for (DBObject o : cursorMap.keySet()) {
// do stuff to init tmp
...
if (tmp < ealiest) {
result = o; // here (new)result --> o . (original)result-->null
ealiest = tmp;
}
// other stuff .....
}
return mergeCount;
}
the result parameter is a pointer. Java does not support pointer-to-pointer as in c/c++.
you can use a class contains a DBObject field, then pass this class to the method.
class Dummy {
DBObject result;
}
modify your getEarliestValue method, replace the argument DBObject result with Dummy dummy,
public static int getEarliestValue(Map<DBObject, DBCursor> cursorMap, Dummy dummy)
and then replace code result = o with dummy.result = o
Dummy dummy = new Dummy();
int mergeCount = MongoTickReaderUtil.getEarliestValue(cursorList, dummy);
DBObject result = dummy.result;
this is a simulation of **ptr in c/c++
You can do the following:
DBObject result= new DBOBject() // or any initializing code;
/** don't try to change or re-initialize reference inside the method
because the result will still point to old one
*/
int mergeCount = MongoTickReaderUtil.getEarliestValue(cursorList, result);
// use the result reference.
This is not specific for java also in the C++ or C pointer unless you use the & with pointer reference.
Note that it seems that the method cann't run alone with out DBObject instance so I think this method should be an instance method of Class DBObject and not a Util method,
For example:
int mergeCount = result.getEarliestValue(cursorList);
So I'm working on a project for my online AP Computer Science class and have run into a problem... Here's the original class definition I was given to build upon (stripped of code irrelevant to my question):
Note: The constructor of the Bin class accepts a single argument, a String. The String is representative of the Bin's name.
import java.util.*;
public class Warehouse
{
// Declare instance variables here
public Warehouse( int binMax )
{
myBinMax = binMax;
myCatalog = new ArrayList<MusicMedia>();
myBins = new ArrayList<Bin>( 5 );
// Code that will start the warehouse
// off with 5 empty bins
}
public void addBin()
{
myBins.add( new Bin( "B" + myBins.size() ) );
}
}
My job is to replace the areas commented out with actual code implementation. While the declaration of the instance variables is very straight forward, I'm rather torn on the best way to implement the code mentioned in the second comment.
In short, which is the best way to complete the above constructor?
Solution A:
private int myBinMax;
private ArrayList<MusicMedia> myCatalog;
private ArrayList<Bin> myBins;
public Warehouse( int binMax )
{
myBinMax = binMax;
myCatalog = new ArrayList<MusicMedia>();
myBins = new ArrayList<Bin>( 5 );
for(int i = 0; i < 5; i++)
{
myBins.add( new Bin( "B" + myBins.size() ) );
}
}
Solution B:
private int myBinMax;
private ArrayList<MusicMedia> myCatalog;
private ArrayList<Bin> myBins;
public Warehouse( int binMax )
{
myBinMax = binMax;
myCatalog = new ArrayList<MusicMedia>();
myBins = new ArrayList<Bin>( 5 );
for(int i = 0; i < 5; i++)
{
addBin(); // <= Is this considered bad practice?
}
}
Solution C:
// Some magical wizard code I would have never thought of. XD
Thanks in advance!
Can you use Java 8?
final List<Bin> bins = IntStream.range(0, 5).
mapToObj(i -> new Bin("Bin" + i)).
collect(Collectors.toCollection(ArrayList::new));
Otherwise, both your solutions looks fine. I would prefer B as you already have an addBin method with one caveat. You must make addBin final as it is a public method. You should only call private or final methods from constructors otherwise you risk someone (possibly you) overriding that method in a subclass and then the subclasses method will be called from the superclass constructor before the subclass is initialized.
try to use a constant than magic numbers in code :)
import java.util.*;
public class Warehouse
{
// Declare instance variables here
private static final int INITIAL_BINS = 5;
private static final String DEFAULT_BINNAME = "DefaultBin_";
public Warehouse( int binMax )
{
myBinMax = binMax;
myCatalog = new ArrayList<MusicMedia>();
myBins = new ArrayList<Bin>( INITIAL_BINS );
for(int i = 0; i < INITIAL_BINS; i++)
{
myBins.add( new Bin( DEFAULT_BINNAME + i ) );
//addBin();// This is fine too depends on how flexible you want naming to be:)
}
}
public void addBin()
{
myBins.add( new Bin( "B" + myBins.size() ) );
}
}
While I agree with most everything that's already been said, the java 8 functional programming style one-liner is pretty sexy. Magic numbers / strings should be avoided. If your class hasn't gone over functional style why not move the entire for loop into its own function? Generally the only thing that a constructor should do is initialize the state of your object. To make that explicit I try to keep to one line per member variable and that one line is nothing more than an initialization statement. IMO methods (which can reference the current state of the object) shouldn't be called from a constructor because your object hasn't been created yet. If you want to add functions to help you initialize your object, feel free to do so but keep them static so that the intent of the function (not referring to the state of the object) is clear.
...
private static final int INITIAL_BIN_COUNT = 5;
private static final String BIN_PREFIX = "B";
private final List<Bin> myBins;
public Warehouse(...) {
....
myBins = initMyBins();
}
private static List<Bin> initMyBins() {
final List<Bin> result = new ArrayList<Bin>(INITIAL_BIN_COUNT);
for(int i = 0; i < INITIAL_BIN_COUNT; i++) {
result.add(new Bin(BIN_PREFIX+i));
}
return result;
}
...
P.S. A few other notes, it's generally good practice to code to interface types rather than concrete class types (maybe you haven't covered interfaces yet, but that's what I did changing ArrayList to List.
I am trying to get the constructor of a variable by using an ASTVisitor.
public boolean visit(VariableDeclarationFragment node)
{
IVariableBinding variableBinding = node.resolveBinding();
// I can't seem to get the constructor here
}
SAMPLE
Base b = new Derived(); // How do I get packageNAME.Derived?
int x = 5; // How do I get 5?
You need to look deeper into the syntax tree to find the answers. The ASTView is a great help in cases like this. This is the update site I use with Kepler: http://www.eclipse.org/jdt/ui/update-site
Your Samples could be answered like this (simplyfied):
/*
* Base b = new Derived(); // How do I get packageNAME.Derived?
*/
private String getClassNameFromConstructor(VariableDeclarationFragment fragment) {
Expression initializer = fragment.getInitializer();
if (initializer instanceof ClassInstanceCreation) {
ClassInstanceCreation instanceCreation = (ClassInstanceCreation)initializer;
if (instanceCreation.getType() instanceof SimpleType) {
SimpleType simpleType = (SimpleType)instanceCreation.getType();
return simpleType.getName().getFullyQualifiedName();
}
}
return null;
}
/*
* int x = 5; // How do I get 5?
*/
private String getInitialisationNumber(VariableDeclarationFragment fragment) {
Expression initializer = fragment.getInitializer();
if (initializer instanceof NumberLiteral) {
NumberLiteral numberLiteral = (NumberLiteral)initializer;
return numberLiteral.getToken();
}
return null;
}
Variables don't have constructors. Objects have constructors. Find the assignment, find the expression being assigned, and if that expression is a constructor you can get the class name from that.
I have this before the process:
protected void onPostExecute(SortedSet<RatedMessage> result) {
List<Object> list=Arrays.asList(result.toArray());
lancon.putExtra("results", list.toArray()); // as serializable
}
then in the other part I have
Object o=this.getIntent().getSerializableExtra("results");
//at this point the o holds the correct value (checked by debugger)
RatedMessage[] rm = (RatedMessage[]) o;// this line hangs out w ClassCastException
resultSet = new TreeSet<RatedMessage>(new Comp());
Collections.addAll(resultSet, rm);
Why I get the ClassCastException?
Finally I got it to work this way:
Serializable s = this.getIntent().getSerializableExtra("results");
Object[] o = (Object[]) s;
if (o != null) {
resultSet = new TreeSet<RatedMessage>(new Comp());
for (int i = 0; i < o.length; i++) {
if (o[i] instanceof RatedMessage) {
resultSet.add((RatedMessage) o[i]);
}
}
}
I'm sorry; I overlooked the use of the no-arg toArray() call.
Please note that there's overloaded toArray(T[]) method that takes an array as an argument.
By using this form, you can control the component type of the array, and it will work as expected.
protected void onPostExecute(SortedSet<RatedMessage> result) {
lancon.putExtra("results", result.toArray(new RatedMessage[result.size()]));
}