I'm trying to create an object using reflection like this
(maybe this is not a good architecture but I'm just doing some tests)
package com.interfaces;
public interface IEmployee {
List<entities.Employee> getEmployees();
}
then
package com.personnel;
public class Employee implements IEmployee{
public Employee(){}
public List<entities.Employee> getEmployees(){
...
}
}
then in another class
package com.factory;
import java.lang.reflect.Constructor;
public final class EmployeeFactory {
private static String path = "com.personnel";
public EmployeeFactory() {}
public static interfaces.IEmployee CreateEmployee(){
String className = path + ".Employee";
try {
Class<?> cls = Class.forName(className);
Object object = cls.newInstance();
return (interfaces.IEmployee)object;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
}
then in another class
public final class PersonnelService {
private static interfaces.IEmployee employeeFactory = com.factory.EmployeeFactory.CreateEmployee();
public List<entities.Employee> getEmployees(){
return employeeFactory.getEmployees();
}
}
finally in my main method
public static void main(String[] args) {
List<entities.Employee> employees = new com.services.PersonnelService().getEmployees();
for(entities.Employee employee : employees){
System.out.println(employee.getEmployeeName());
}
when the code arrives to Class cls = Class.forName(className) it throws the exception message "com.personnel.Employee.(java.lang.String)" with "NoSuchMethodException".
UPDATE
I found the problem. In fact, it was a bad naming convention. I had a serializable Employee class (my pojo) and an Employee class which implements my interface IEmployee which is intended to perform DB operations. I renamed this last one as EmployeeDAL and it worked. Even I defined the full class name as you can see in the example, it looks like that I was trying to instantiate my serializable pojo (I still don't get it, but...). Thanks for your time.
I tried to reproduce your code and not found any error on this code. I only get NoSuchMethodException message when I change Employee constructor with String argument
package com.agit.example.test;
import java.util.ArrayList;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List<Employee> employees = new PersonnelService().getEmployees();
for(Employee employee : employees){
System.out.println(employee);
}
}
}
interface IEmployee {
List<Employee> getEmployees();
}
class Employee implements IEmployee{
public Employee(){}
public List<Employee> getEmployees(){
List<Employee> x = new ArrayList<>();
x.add(this);
return x;
}
}
final class EmployeeFactory {
private static String path = "com.agit.example.test";
public EmployeeFactory() {}
public static IEmployee CreateEmployee(){
String className = path + ".Employee";
try {
Class<?> cls = Class.forName(className);
Object object = cls.newInstance();
return (IEmployee)object;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
}
final class PersonnelService {
private static IEmployee employeeFactory = EmployeeFactory.CreateEmployee();
public List<Employee> getEmployees(){
return employeeFactory.getEmployees();
}
}
Related
I have this class
public static class ExampleClass {
private ExampleObject exampleObject;
public ExampleObject getExampleObject() {
return exampleObject;
}
public void setExampleObject(ExampleObject exampleObject) {
this.exampleObject = exampleObject;
}
}
public class ExampleObject {
private String exampleProp;
public String getExampleProp() {
return exampleProp;
}
public void setExampleProp(String exampleProp) {
this.exampleProp = exampleProp;
}
}
I created a loop to get field types of ExampleClass. How can I create a new instance using field type on runtime? My field type is:
I try this
Class c = Class.forName(fieldType.getName());
var ins = c.newInstance();
But code throws exception like this
java.lang.NoSuchMethodException: com.example.Main$ExampleObject.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3350)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2554)
at com.example.CreatorFactory.getCreator(CreatorFactory.java:29)
at com.example.AutoFill.fill(AutoFill.java:15)
at com.example.AutoCreate.<init>(AutoCreate.java:15)
at com.example.AutoCreate.build(AutoCreate.java:23)
at com.example.Main.main(Main.java:7)
I am trying to access the method GetDatbaseName(), from the returned object obj, but it is returning error that the method is not available.
However, when I Typecast the obj, it is working.
String name = ((Oracle)obj).GetDatabaseName();
How to handle this generic? Like I can't typecast for each return type like Oracle and MongoDB. Also any better implementation for this?
// one class needs to have a main() method
public class HelloWorld
{
// arguments are passed using the text field below this editor
public static void main(String[] args)
{
Data dt = new Data("Oracle");
Object obj = dt.GetObject();
String name = obj.GetDatabaseName();
System.out.println(name);
}
}
public class Data
{
public String _type;
public Data(String type)
{
_type = type;
}
public Object GetObject()
{
Object obj = null;
switch(_type)
{
case("Oracle"):
obj = new Oracle("Test");
break;
case("MongoDB"):
obj = new MongoDB("TestCollection");
break;
}
return obj;
}
}
public class Oracle
{
public String _databaseName;
public Oracle(String databaseName)
{
_databaseName = databaseName;
}
public String GetDatabaseName() { return _databaseName; }
}
public class MongoDB
{
public String _collectionName;
public MongoDB(String collectionName)
{
_collectionName = collectionName;
}
public String GetCollectionName() { return _collectionName; }
}
There are two ways to solve this, the first is using a generic class, while the second is using interface, the second approach is better if you know that the classes will have the same methods, while the generic approach is if the classes have different methods
Generic approach
public class DBtest{
public static void main(String[] args){
DataBase<Oracle> database = new DataBase<>(Oracle.class);
Oracle oracle = database.getDataBase();
System.out.println(oracle.getDatabaseName());
}
}
class DataBase<T>{
private T database;
public DataBase(Class<T> classOfT){
try {
database = classOfT.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public T getDataBase(){
return database;
}
}
class Oracle{
private String _databaseName;
public Oracle(){
_databaseName = "test";
}
public String getDatabaseName() { return _databaseName; }
}
As you can see, it is not possible to define the name of the database, this would be possible of you write <T extends Name> which is an interface which has getName() and setName() method
Interface approach
public class DBtest{
// arguments are passed using the text field below this editor
public static void main(String[] args){
DataBase database = new DataBase(new Oracle("test"));
DatabaseName databaseName = database.getDataBase();
System.out.println(databaseName.getName());
}
}
interface DatabaseName {
String getName();
}
class DataBase{
private DatabaseName databaseName;
public DataBase(DatabaseName databaseName){
this.databaseName = databaseName;
}
public DatabaseName getDataBase(){
return databaseName;
}
}
class Oracle implements DatabaseName {
private String _databaseName;
public Oracle(String name){
_databaseName = name;
}
public String getName() {
return _databaseName;
}
}
class MongoDB implements DatabaseName {
private String _databaseName;
public MongoDB(String name){
_databaseName = name;
}
public String getName() {
return _databaseName;
}
}
Obviously DatabaseName is a bad name for an interface, but it is the only method which is the same for both classes, so it makes sense to call it that. The great thing about interfaces is that you don't have to give a shit about what class is used as long as you know the method names.
You problem is on the following lines:
Object obj = dt.GetObject();
String name = obj.GetDatabaseName();
As far as those lines are concerned, obj is of type Object, which does not have the invoked method; thus, the issue. This is due to Java being strongly typed.
To go around that, you need a type that has this method, or use reflection. To use a type that has this method, they need to inherit it from a common parent of implement it from a common interface. You can also wrap you objects or a bunch of other alternatives.
In your case, it seems that a common interface is the easiest way to go. In this case, each class should implement this interface and instead of using Object your reference would be of the type of that interface.
public Object GetObject()
Would become
public MyInterface GetObject()
and
public class Oracle
would be
public class Oracle implements MyInterface
Where MyInterface would declare the method
public interface MyInterface {
String GetDatabaseName();
}
Being mindful of Java conventions, methods should start with lowercase
public interface MyInterface {
String getDatabaseName();
}
In the case where you cannot change the code in order to implements those methods, you can use "instanceof" to test against the class type.
name = (obj instanceof Oracle)?((Oracle)obj).GetDatabaseName():((MongoDB )obj).getCollectionName();
You must have to create an Interface and then with getDatabaseName() method. Then your objects Oracle and MongoDB must implement that interface.
What you are trying to do is something similar to AbstractFactory Pattern. You should google it.
public interface MyDbInterface {
String getDatabaseName();
}
public class HelloWorld {
// arguments are passed using the text field below this editor
public static void main(String[] ){
MyDbInterface dt = DataFactory.create("Oracle");
String name = dt.getDatabaseName();
System.out.println(name);
}
}
public final class DataFactory{
private DataFactory(){
super();
}
public static MyDbInterface create(String type){
MyDbInterface obj = null;
switch(type) {
case("Oracle"):
obj = new Oracle("Test");
break;
case("MongoDB"):
obj = new MongoDB("TestCollection");
break;
}
return obj;
}
}
public class Oracle implement MyDbInterface{
public String databaseName;
public Oracle(String databaseName){
databaseName = databaseName;
}
#Override
public String getDatabaseName() {
return databaseName;
}
}
public class MongoDB implement MyDbInterface{
public String collectionName;
public MongoDB(String collectionName){
collectionName = collectionName;
}
public String getCollectionName() {
return collectionName;
}
#Override
public String getDatabaseName() {
return getCollectionName();
}
}
I suposed you come from C#, check java style guide. ;)
You should think about the design of your code. You need to use basic OOP principal to solve the problem. There are several ways to solve your problem like using interface/generics etc. Here I am giving one such example.
public class HelloWorld {
public static void main(String[] args) {
Data dt = new Data("Oracle");
DataBase obj = dt.GetObject();
String name = obj.getDatabaseName();
System.out.println("Name : "+name);
}
}
class Data {
public String _type;
public Data(String type) {
_type = type;
}
public DataBase GetObject() {
DataBase dataBase=null;
switch (_type) {
case "Oracle":
dataBase = new Oracle();
break;
case "Mongo":
dataBase = new MongoDb();
break;
}
return dataBase;
}
}
interface DataBase {
String getDatabaseName();
}
class Oracle implements DataBase {
public String getDatabaseName() {
return "Oracle";
}
}
class MongoDb implements DataBase {
public String getDatabaseName() {
return "Mongo";
}
}
Edited:
Here is another way to solve your problem. I believe this approach might solve your problem.
public class HelloWorld {
public static void main(String[] args) {
Data<Oracle> dt = new Data<Oracle>("Oracle");
Oracle obj = dt.getObject();
String name = obj.getDatabaseName();
System.out.println("Name : "+name);
}
}
class Data<T> {
public String _type;
public Data(String type) {
_type = type;
}
public T getObject() {
Object dataBase=null;
switch (_type) {
case "Oracle":
dataBase = new Oracle();
break;
case "Mongo":
dataBase = new MongoDb();
break;
}
return (T)dataBase;
}
}
class Oracle {
public String getDatabaseName() {
return "Oracle";
}
}
class MongoDb {
}
I want to define a function that creates different type objects that share the same base class. I'd like to pass in the object type and have the function creating the object and then modifying its attributes. The problem is that the main class from which all these objects are created, does not have the object's attributes so the code fails to compile.
Example:
public void new_generic_report(Class report_class, String report_name) {
Report new_report = this.reportManager.createReport(report_class);
new_report.set_name(report_name);
}
Calling new_generic_report(GreenReport.class, "green_report"); fails because new_report is of the class Report instead of GreenReport so it does not have the .set_name method.
I know I could implement the .set_name method (and other common methods) in the main Report class but I am writing code to interface with an API that I cannot modify.
If you are sure that createReport returns an instance of the correct class you can just do a cast:
((SpecialClass)new_report).set_name(report_name);
An alternative is to use reflection:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
static class Base {};
static class Child extends Base {
public void setName(final String name) {
System.out.println("setName("+name+")");
}
}
public static void main(String[] args) {
new Test().new_generic_report(Child.class, "Testname");
}
public void new_generic_report(final Class clazz, final String name) {
Base base = createBase(clazz);
try {
Method m = clazz.getMethod("setName", String.class);
m.invoke(base, name);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private Base createBase(Class report_class) {
return new Child();
}
}
Of course this only works, if the returned instance implements the method.
Create a parent class for your report for instance :
public abstract class NamedReport extends Report
{
public abstract void setName(String name);
}
class GreenReport extends NamedReport {
#Override
public void setName(String name) {
}
}
Then simply cast your class in your method :
public void new_generic_report(Class report_class, String report_name) {
Report new_report = this.reportManager.createReport(report_class);
if (new_report instanceof NamedReport)
{
((NamedReport)new_report).set_name(report_name);
}
}
I have a parent class like :
public abstract class ParentObject {
public abstract String[] fields();
public abstract String tableName();
}
And a child class like this :
public class MyObject extends ParentObject {
String id = "";
String name = "";
public MyObject(Map<String, Object> map){
this.id = map.get("id").toString();
this.name = map.get("name").toString();
}
#Override
public String[] fields() {
return new String[]{"id","name"};
}
#Override
public String tableName() {
return "testTable";
}
}
I want to create a Builder Class in ParentObject to be able to init all the child classes like :
MyObject object = new MyObject.Builder().getById("objectId").build();
I tried using generics but i could not find what i was looking for i need a Builder class like :
public static class Builder{
public Builder(){
}
public Builder getById(){
//some server codes here
return Builder.this;
}
public T build(){
return new T(map);
}
}
I need to know if i can use generics like this :
return new T();
If no, how can i do it ?
Basically you need something like the following:
public <T extends ParentObject> T build(Class<T> targetClass) throws IllegalAccessException, InstantiationException {
T t = targetClass.newInstance();
t.tableName(); // can access the method
return t;
}
You need to pass in the targetClass like build(MyObject.class).
i have a question concerning Json deserialization with Jackson (edit: 2.0.4 version). I would like serialize an Bean, containing the list of other beans, as string , save this string and then to deserialize later this string. I use the some base class and its subtypes. The basis class Parent is an abstract class, that has two attributes with getters und setters, this class has also an abstract method getType(). Other abstract class AbstractChild inherits from class Parent . This class has attributes too and isExportEnabled() abstract method.
I have no problems, if this Bean will be serialized. I use the following annotation on the Parent class
*#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#cls")*
The string will be generated.
But the desirialize failed: the exception “Unrecognized field "type"”will be thrown. But I need this attribute! I’m tried to set #JsonProperty("type") on abstract method, this has no effect.
Please help me.
edit: if i introduce the private fields "type" (Parent) and "exportEnabled" (AbstractChild) so it runs correctly.
P.S The exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "type" (class tst.SimpleTestMain$FirstChild), not
marked as ignorable (4 known properties: , "id", "maxCount", "code",
"minCount"]) at [Source: java.io.StringReader#1ad9fa; line: 1,
column: 125] (through reference chain:
tst.Fam["members"]->tst.FirstChild["type"]) at
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
at
com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:568)
…and the example class
package tst;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SimpleTestMain {
enum Type {
TYPE_A, TYPE_B
}
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#cls")
public static abstract class Parent {
private int id;
private String code;
public Parent() {
}
#JsonProperty("type")
// First abstract getter
public abstract Type getType();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
public static abstract class AbstractChild extends Parent {
private int minCount;
private int maxCount;
public AbstractChild() {
}
// Second abstract method: boolean used
public abstract boolean isExportEnabled();
public int getMinCount() {
return minCount;
}
public void setMinCount(int minCount) {
this.minCount = minCount;
}
public int getMaxCount() {
return maxCount;
}
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
}
}
public static class FirstChild extends AbstractChild {
#Override
public boolean isExportEnabled() {
return false;
}
#Override
public Type getType() {
return Type.TYPE_A;
}
}
public static class SecondChild extends AbstractChild {
#Override
public boolean isExportEnabled() {
return true;
}
#Override
public Type getType() {
return Type.TYPE_B;
}
}
public static class Fam {
private int famId;
private List<Parent> members;
public Fam() {
members = new ArrayList<Parent>();
}
public int getFamId() {
return famId;
}
public void setFamId(int famId) {
this.famId = famId;
}
public List<Parent> getMembers() {
return members;
}
public void setMembers(List<Parent> members) {
this.members = members;
}
public void addMember(Parent member) {
members.add(member);
}
}
public SimpleTestMain() {
}
public static void main(String[] args) {
Fam fam = new Fam();
FirstChild fc = new FirstChild();
fc.setId(1);
fc.setCode("FirstChildCode");
fc.setMinCount(1);
fc.setMaxCount(4);
fam.addMember(fc);
SecondChild sc = new SecondChild();
sc.setCode("SecondChildCode");
sc.setMinCount(131);
sc.setMaxCount(431);
fam.addMember(sc);
String test = "";
// Serialize it
ObjectMapper mapper = new ObjectMapper();
try {
test = mapper.writeValueAsString(fam);
System.out.println("Serialized bean:\n" + test);
// the output
// Serialized bean:
// {"famId":0,"members":[{"#cls":".SimpleTestMain$FirstChild","id":1,"code":"FirstChildCode","minCount":1,"maxCount":4,"type":"TYPE_A","exportEnabled":false},{"#cls":".SimpleTestMain$SecondChild","id":0,"code":"SecondChildCode","minCount":131,"maxCount":431,"type":"TYPE_B","exportEnabled":true}]}
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize it
mapper = new ObjectMapper();
// mapper.enableDefaultTyping();
try {
Fam fam1 = mapper.readValue(test, Fam.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}