Call virtual method in constructor - better design - java

I declared an ISerializable interface in java.
I basically have 2 methods: serialize(), and deserialize(byte[] buffer).
public interface ISerializable{
byte[] serialize();
deserialize(byte[] buffer);
}
and here is an example of a class implementing this interface:
public class MySerializableClass implements ISerializable{
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
Ideally, I would like the call to deserailize to be implicit. i.e. when calling the constructor MySerializableClass(byte[] buffer), it would call the correct deserialize with the buffer passed. like that:
public abstract class AbstractSerializable {
public abstract byte[] serialize();
public abstract void deserialize(byte[] buffer);
public AbstractSerializable (){}
public AbstractSerializable (byte[] buffer){
deserialize();
}
}
public class MySerializableClass extends AbstractSerializable {
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
AFAIK it is problematic to call virtual methods within the constructor and this might end up with an undefined behavior.
so currently, I am doing the following:
MySerializableClass myClass = new MySerializableClass();
myClass.deserialize(buffer);
or by using a dedicated static method that is defined for each class that extends my interface (and basically just do the above 2 lines of code):
MySerializableClass myClass = MySerializableClass.CreateMySerializableClass(buffer);
My questions is: is there any elegant way to do that without the need to define a dedicated static method for each class implements ISerializable? Is there any design pattern that solves this issue?
Note: My serialization is unique so I need to write it on my own, and also for technical reasons I can only use very basic features of Java. ( no annotations,templaates metadata, etc.) so I need a very basic OOP solution.

I find your solution elegant enough, what you're doing is a Factory, which is an elegant way to solve your problem. You can keep your constructor private, and always retrieve the objects through the factories
public class MySerializableClass extends AbstractSerializable {
private MySerializableClass(){
}
public static MySerializableClass CreateMySerializableClass(final byte[] buffer){
MySerializableClass result = new MySerializableClass();
result.deserialize(buffer)
return result;
}
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}

One another solution is you remove no-argument constructor so your concrete class must have to initialize with argument constructor .

I wouldn't pass byte arrays around when doing the serialization - instead I would use java.io.DataOutput and java.io.DataInput. You could then declare the interface ISerializable like e.g. so:
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
Then you could provide static utility methods, that are able to serialize and deserialize instances of ISerializable when provided with some DataOutput/DataInput. The static deserialize method could then also call a possible constructor, that accepts a DataInput as its only argument.
Here is a complete example code for this approach, that also includes a main method for testing the serialization:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SerializableTest {
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
/**
* Writes the given ISerializable to the given DataOutput.
*/
public static void writeSerializable(ISerializable s, DataOutput out) throws IOException{
writeClass(out, s.getClass());
s.serialize(out);
}
/**
* Reads an ISerializable from the given DataInput.
*/
public static ISerializable readSerializable(DataInput in, ClassLoader cl) throws IOException{
ISerializable element = null;
Class<?> c;
try {
c = readClass(in, cl);
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
try {
try {
// see if the class has a constructor that accepts a DataInput
Constructor<?> constructor= c.getDeclaredConstructor(DataInput.class);
constructor.setAccessible(true);
return (ISerializable)constructor.newInstance(in);
} catch (NoSuchMethodException e) {
//ignore
}
element = (ISerializable) newInstance(c);
element.deserialize(in);
} catch (Exception e) {
throw new IOException("Could not deserialize the class" + c.getName());
}
return element;
}
private static <T> T newInstance(Class<T> c) throws IOException {
T element = null;
Constructor<T> constructor;
try {
constructor = c.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
element = constructor.newInstance();
} catch (NoSuchMethodException | InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new IOException(e);
}
return element;
}
private static void writeClass(DataOutput out, Class<?> c) throws IOException {
out.writeUTF(c.getName());
}
private static Class<?> readClass(DataInput in, ClassLoader cl) throws IOException, ClassNotFoundException {
String name = in.readUTF();
return cl.loadClass(name);
}
// some test classes for testing serialization in the main method
public static class TestClass implements ISerializable{
private String data;
protected TestClass() {
// ISerializable no argument constructor
super();
}
public TestClass(String data) {
super();
this.data = data;
}
#Override
public void serialize(DataOutput out) throws IOException {
out.writeUTF(data);
}
#Override
public void deserialize(DataInput in) throws IOException {
this.data = in.readUTF();
}
}
public static class TestClass2 implements ISerializable{
private final String data;
protected TestClass2(DataInput in) throws IOException {
// ISerializable DataInput constructor
super();
this.data = in.readUTF();
}
public TestClass2(String data) {
super();
this.data = data;
}
#Override
public void serialize(DataOutput out) throws IOException {
out.writeUTF(data);
}
#Override
public void deserialize(DataInput in) throws IOException {
throw new UnsupportedOperationException();
}
}
// tests serialization and deserialization of two test classes
public static void main(String[] args) {
TestClass t1 = new TestClass("TestClass 1");
TestClass2 t2 = new TestClass2("TestClass 2");
File file = new File("testfile");
if (file.exists()) {
file.delete();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return;
}
DataOutputStream out = null;
try {
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
writeSerializable(t1, out);
writeSerializable(t2, out);
} catch (IOException e) {
e.printStackTrace();
return;
}finally{
if (out != null) {
try {
out.close();
} catch (IOException e) {}
}
}
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
ClassLoader cl = SerializableTest.class.getClassLoader();
TestClass loadedClass1 = (TestClass) readSerializable(in, cl);
TestClass2 loadedClass2 = (TestClass2) readSerializable(in, cl);
System.out.println("loadedClass1.data: " + loadedClass1.data);
System.out.println("loadedClass2.data: " + loadedClass2.data);
} catch (IOException e) {
e.printStackTrace();
return;
} finally{
if (in != null) {
try {
in.close();
} catch (IOException e) {}
}
}
}
}
Of course you will have the memory overhead of storing the class name together with the data, when using the static methods. But you can still call the serialize and deserialize methods manually, if this is a problem.

in my opinion you don't need to implement ISerializable interface with methods: serialize and deserialize. in any serializable class.
I think it would be better to have
interface ISerializer
{
byte[] serialize(ISerializable serializable);
ISerializable deserialize(byte[] buffer);
}
and have Serializer class which implements this ISerializer interface
ISerializable _ what to serialize or deserialize.
ISerializable will have methods, what serialization and deserialization need.
if serialization and deserialization don't need anything, it can be Object instead of ISerializable.
if you don't want casts after using deserialize method, this deserialize method can be generic:
T deserialize<T>( byte[] buffer );

Related

Unable to serialize instance variable of a non-serializable superclass from the serializable subclass

New to this topic and right now I'm stuck at a brick wall. I have 2 classes, parent class: Controller.java and subclass: GreenhouseControls.java. I need to serialize a GreenhouseControls object but also an instance variable (eventList) from its superclass Controller.java.
My serialization happens when an inner class of GreenhouseControls.java throws a custom ControllerException, which is caught in the main method. Before terminating the program, the GreenhouseControls object should be saved (including the field from its superclass).
Why is a NotSerializableException thrown by the inner class WindowMalfunction of GreenhouseControls? Anyone have any ideas, as I am seriously stuck?
What I tried is the following:
Implement serializable on Controller.java. This is because if the superclass is serializable, then subclass is automatically serializable, however this throws java.io.NotSerializableException: GreenhouseControls$WindowMalfunction, (WindowMalfunction is the inner class that throws the initial exception to begin the serialization processs).
Implement serializable on GreenhouseControls.java and implement custom serialization by overriding writeObject() and readObject() to save the field from the superclass. This approach yet again throws the same exception as the approach 1.
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(super.eventList);
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
Object obj = in.readObject();
List<Event> x = cast(obj);
super.eventList = x;
}
Controller.java
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class Controller {
// THIS IS THE VARIABLE I NEED TO SAVE
protected List<Event> eventList = new ArrayList<Event>();
public void addEvent(Event c) {
eventList.add(c);
}
public void run() throws ControllerException {
while (eventList.size() > 0)
// Make a copy so you're not modifying the list
// while you're selecting the elements in it:
for (Event e : new ArrayList<Event>(eventList))
if (e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
public static void shutDown() { }
}
GreenhouseControls.java class (note I have removed the inner classes and other code from it and only left related info)
public class GreenhouseControls extends Controller implements Serializable {
private int errorcode = 0;
public class WindowMalfunction extends Event {
public WindowMalfunction(long delayTime) {
super(delayTime);
}
public void action() throws ControllerException {
windowok = false;
throw new ControllerException("Window malfunction");
}
public String toString() {
return "Window malfunction";
}
}
public class PowerOut extends Event {
public PowerOut(long delayTime) {
super(delayTime);
}
public void action() throws ControllerException {
poweron = false;
throw new ControllerException("Power out");
}
public String toString() {
return "Power out";
}
}
// Various other inner classes that extend event exist
public static void serializeObject(GreenhouseControls gc) {
FileOutputStream fileOut;
ObjectOutputStream out;
try {
fileOut = new FileOutputStream("dump.out");
out = new ObjectOutputStream(fileOut);
out.writeObject(gc);
System.out.println("WERRROR code: " + gc.getError());
out.close();
fileOut.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(super.eventList);
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
Object obj = in.readObject();
List<Event> x = cast(obj);
super.eventList = x;
}
#SuppressWarnings("unchecked")
public static <T extends List<?>> T cast(Object obj) {
return (T) obj;
}
public int getError() {
return errorcode;
}
public Fixable getFixable(int errorcode) {
switch (errorcode) {
case 1:
return new FixWindow();
case 2:
return new PowerOn();
default:
return null;
}
}
public static void main(String[] args) {
GreenhouseControls gc = null;
try {
String option = args[0];
String filename = args[1];
if (!(option.equals("-f")) && !(option.equals("-d"))) {
System.out.println("Invalid option");
printUsage();
}
// gc = new GreenhouseControls();
if (option.equals("-f")) {
gc = new GreenhouseControls();
gc.addEvent(gc.new Restart(0, filename));
}
gc.run();
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid number of parameters");
printUsage();
} catch (ControllerException e) {
String errormsg;
if (e.getMessage().equals("Window malfunction")) {
gc.errorcode = 1;
errormsg = "Window malfunction event occurred Error code: " + gc.errorcode;
} else {
gc.errorcode = 2;
errormsg = "Power out event occurred Error code: " + gc.errorcode;
}
logError(errormsg);
serializeObject(gc);
gc.displayEventList();
shutDown();
}
}
}
Event.java
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
public void start() { // Allows restarting
eventTime = System.currentTimeMillis() + delayTime;
}
public boolean ready() {
return System.currentTimeMillis() >= eventTime;
}
public abstract void action() throws ControllerException;
Event has to be Serializable too.
Change
public abstract class Event {
to
public abstract class Event implements Serializable {

use a customize method on an Object

I've created an Interface named "Writeable", and every class that implements it should override the method returnFilePath - which returns a String - a path to the correct file path related to that class.
for Example:
public class PatientRepository extends Observable implements Writeable,Readable{
private static final String filepath="src/files/patients.det";
...
#Override
public String returnFilePath() {
return filepath;
}
as you can see, this class also extends Observable. that class is observed by FileManager class:
public class FileManager implements Observer {
#Override
public void update(Observable o, Object arg) {
try (OutputStream fileOutputStream = new FileOutputStream(????);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)){
objectOutputStream.writeObject(arg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
what i want to do is that in the FileOutputStream constructor i'll pass the correct file path, according to the Object i'm currently working on, but I can't get access to the returnFilePath() method i wrote.
what should i do to fix that?
All i had to do is casting:
public class FileManager implements Observer {
#Override
public void update(Observable o, Object arg) {
try (OutputStream fileOutputStream = new FileOutputStream( ((Writeable)o).returnFilePath());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)){
objectOutputStream.writeObject(arg);
} catch (IOException e) {
e.printStackTrace();
}
}
}

How is it possible to override final writeObject() method in Custom Serialization (Java)

I'm learning about custom serialization and I don't understand how is it possible to overide the 2 methods writeObject() and readObject() because I know these 2 methods are final, and I know that final methods cannot be overriden.
writeObject() method from ObjectOutputStream:
public final void writeObject(Object obj) throws IOException
And writeObject() needs to be overriden like here:
private void writeObject(ObjectOutputStream output) throws IOException
I understand that the new writeObject() method is private, but it is called using reflection by Java serialization mechanism. But I don't understand how is it possible to override a final method.
Account Class:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID = 154754873L;
String userName = "durga";
transient String psw = "anushka";
private void writeObject(ObjectOutputStream output) throws IOException {
output.defaultWriteObject();
output.writeObject(psw);
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
input.defaultReadObject();
psw = (String) input.readObject();
}
}
SerializationDemo Class:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializationDemo {
public void serialize(Account a1, String fileName) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
oos.writeObject(a1);
} catch (FileNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
} catch (IOException ex) {
System.out.printf("ERROR: %s", ex);
}
}
public Account deserialize(String fileName) {
Account a2 = null;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("account.ser"))) {
a2 = (Account) ois.readObject();
} catch (FileNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
} catch (IOException | ClassNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
}
return a2;
}
}
SerializationApp Class:
public class SerializationApp {
public static void main(String args[]) {
Account a1 = new Account();
System.out.println(a1.userName + " " + a1.psw);
SerializationDemo demo = new SerializationDemo();
demo.serialize(a1, "account.ser");
Account a2 = demo.deserialize("account.ser");
System.out.println(a2.userName + " " + a2.psw);
}
}
It is not possible to override a final method.
But you don't need to.
One way to customize serialization is to provide writeObject and readObject methods (like you did):
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
Note that we don't talk about implementing or overriding methods here.
This should be actually enough - I wonder why you need to override writeObject in ObjectOutputStream.
If you want to customize serialization by subclassing the ObjectOutputStream, you still can "override" how objects are written. For this, subclass ObjectOutputStream, in the subclass call super() and override writeObjectOverride. If you call the protected ObjectOutputStream() constructor, the enableOverride flag is set to true and the final method writeObject (which you can't override) delegates to writeObjectOverride (which can be overridden).
You are confused.
ObjectOutputStream.writeObject() is final and you cannot override it.
The private void writeObject(ObjectOutput) method you can use for custom serialization goes into your own Serializable class, not into a class that extends ObjectOutputStream. The question of overriding does not arise.

If a class is loaded multiple times, do its static-members get initialized multiple times?

If a class is loaded multiple times, do its static-members get initialized multiple times?
How do I check for that?
If there are different classloaders involved, then they will be completely separate classes, with separate static fields etc - and each will be initialized separately.
(The simplest way to diagnose this is just to log when you get initialized, of course...)
static {
// Log initialization
}
The code inside static block is executed only once: the first time you make an object of that class or the first time you access a static member of that class (even if you never make an object of that class). This mean its invoked when the class loader loads the class to memory. So its per class loader. If you have multiple class loaders each one will have its own copy of the classes so static block will be invoked by each class loaders. To test this you can put a Sysout in a static block an try and load it using a custom class loader. In the below example the static block will be executed twice. One by the system class loader when we run the static main method and then by our custom class loader.
package sample;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Sample {
static {
System.out.println("Entered Static Block!!");
}
public static void main(String[] args) {
CustomClassLoader loader = new CustomClassLoader();
try {
Class<?> c = loader.findClass("sample.Sample");
Object o = c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class CustomClassLoader extends ClassLoader {
private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
#Override
public String toString() {
return CustomClassLoader.class.getName();
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classes.containsKey(name)) {
return classes.get(name);
}
byte[] classData;
try {
classData = loadClassData(name);
} catch (IOException e) {
throw new ClassNotFoundException("Class [" + name
+ "] could not be found", e);
}
Class<?> c = defineClass(name, classData, 0, classData.length);
resolveClass(c);
classes.put(name, c);
return c;
}
private byte[] loadClassData(String name) throws IOException {
BufferedInputStream in = new BufferedInputStream(
ClassLoader.getSystemResourceAsStream(name.replace(".", "/")
+ ".class"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
out.write(i);
}
in.close();
byte[] classData = out.toByteArray();
out.close();
return classData;
}
}
The following actions do not cause a class to be loaded:
Referring to a static final primitive field that is known at compile time.
classLoader.getResource(className.replace('.', '/') + ".class")
The following cause a class to be loaded (i.e., the .class file is parsed, and a Class<?> object is created):
Any line of code that refers to the class symbolically e.g. Foo.class
Class.forName(String, false, ClassLoader)
ClassLoader.loadClass(String)
Loading a subclass or array of the class, or initializing a class whose code or method signatures refer to the class.
The following cause a class to be initialized (i.e., the static blocks are executed):
Constructing a new instance
Calling a static method
Getting or setting a static field that is not a compile-time constant.
Class.forName(String, true, ClassLoader) and Class.forName(String)
If you do initialize the class from multiple ClassLoaders, the static blocks are indeed executed multiple times. For example, the following code:
import java.net.URL;
import java.net.URLClassLoader;
public class InitializeClassMultipleTimes {
static class Foo {
static {
System.out.format(" %s initialized by %s%n", Foo.class.getSimpleName(), Foo.class.getClassLoader());
}
public static void foo() {}
}
private static Class<Foo> loadClass() {
System.out.println("Loading class.");
// Load the .class file. This will fail if the class file is gone or has
// the wrong file format.
return Foo.class;
}
private static void initializeClass(Class<?> innerClass) {
System.out.println("Initializing class");
try {
Class.forName(innerClass.getName(), true, innerClass.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
public static void main(String... argv) throws ClassNotFoundException {
Class<Foo> fooClass = loadClass();
initializeClass(fooClass);
URLClassLoader myClassLoader = ((URLClassLoader) InitializeClassMultipleTimes.class.getClassLoader());
URL[] urls = myClassLoader.getURLs();
for (int i = 0; i < 2; i++) {
URLClassLoader newClassLoader = new URLClassLoader(urls, null); // parent=bootstrap
System.out.format("%nLoading class using another class loader%n", Foo.class.getSimpleName());
Class<?> fooClassAgain = Class.forName(fooClass.getName(), false, newClassLoader);
initializeClass(fooClassAgain);
}
}
}
produces the following output. Note that you can also run it under strace -f -e file to verify when the .class files are read.
Loading class.
Initializing class
Foo initialized by sun.misc.Launcher$AppClassLoader#73d16e93
Loading class using another class loader
Initializing class
Foo initialized by java.net.URLClassLoader#15db9742
Loading class using another class loader
Initializing class
Foo initialized by java.net.URLClassLoader#7852e922

call the same method name from instances different type

I have some libraries from external company, I want to use this API. I try to implement calling this API, my logic should call the same method name. I have duplicate codes, I want to avoid to do this. I'm beginner and subjects like interfaces, polymorphism are little bit difficult to me.
public void modPeople(Object person)
{
if (person instanceof com.company.persontype1)
{
com.company.persontype1 fireman = (com.company.persontype1) person;
String name = fireman.getName();
if (name!=null ) {
...
fireman.set_name();
fireman.save();
}
permissions = fireman.get_Permissions();
...
permissions = fixperm (permissions);
fireman.set_Permissions();
};
if (person instanceof com.company.persontype2)
{
com.company.persontype2 nurse = (com.company.persontype2) person;
String name = nurse.getName();
if (name!=null ) {
...
nurse.set_name();
nurse.save();
}
permissions = nurse.get_Permissions();
...
permissions = fixperm (permissions);
nurse.set_Permissions();
};
}
First of all I should mention that the methodology which you requested in your question is called "Duck Typing". Generally this technology is possible in Java (see below the example) but it's not widely used in Java. There could be performance hits etc. It would be much better to introduce a proper inheritance/interface level instead.
Also the provided example don't deal with exceptions properly etc. It's just a quick and quite dirty "demostration of the technology". Feel free to adapt it for your needs.
It's Java7 (for multi-catch clauses, you may refactor this with ease).
ISomeIterface.java (it contains all common methods implemented by classes which are used in your "bad code"):
package org.test;
public interface ISomeInterface {
public String getName();
public void setName(String _name);
public void save();
// specify other common methods
}
ReflectCaller.java:
package org.test1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import org.test.ISomeInterface;
public class ReflectCaller {
private final Method[] methods = ISomeInterface.class.getDeclaredMethods();
private final Map<Class<?>, Method[]> maps = new HashMap<Class<?>, Method[]>();
public void inspectClass(Class<?> _clazz) throws NoSuchMethodException, SecurityException {
final Method[] ms = new Method[methods.length];
int i = 0;
for(final Method m: methods) {
ms[i] = _clazz.getMethod(m.getName(), m.getParameterTypes());
i++;
}
maps.put(_clazz, ms);
}
public ISomeInterface wrapper(Object _obj) {
final Method[] ms = maps.get(_obj.getClass());
// To be replaced by guava's Preconditions.checkState()
if (ms == null)
throw new NoSuchElementException(String.format("Class %s is unregistered", _obj.getClass().getName()));
return new SomeInterfaceImpl(_obj, ms);
}
private static class SomeInterfaceImpl implements ISomeInterface {
private final Object obj;
private final Method[] ms;
public SomeInterfaceImpl(Object _obj, Method[] _ms) {
ms = _ms;
obj = _obj;
}
#Override
public String getName() {
try {
return (String) ms[0].invoke(obj);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
#Override
public void setName(String _name) {
try {
ms[1].invoke(obj, _name);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
#Override
public void save() {
try {
ms[2].invoke(obj);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
And test class ReflectTest.java. Notice that classes ReflectTest.Test and ReflectTest.Test2 has the same methods as ISomeInterface but don't implement it, they are completely independent from that interface and from each other.
package org.test2;
import org.test.ISomeInterface;
import org.test1.ReflectCaller;
public class ReflectTest {
private final ReflectCaller rc;
ReflectTest(Class ... _classes) throws NoSuchMethodException, SecurityException {
rc = new ReflectCaller();
for(final Class c: _classes)
rc.inspectClass(c);
}
void callSequence(Object _o) {
// this function demonstrates the sequence of method calls for an object which has "compliant" methods
ISomeInterface tw = rc.wrapper(_o);
tw.setName("boo");
System.out.printf("getName() = %s\n", tw.getName());
tw.save();
}
public static class Test {
public String getName() {
System.out.printf("%s.getName()\n", getClass().getName());
return "boo";
}
public void setName(String _name) {
System.out.printf("%s.setName(%s)\n", getClass().getName(), _name);
}
public void save() {
System.out.printf("%s.save()\n", getClass().getName());
}
}
public static class Test2 {
public String getName() {
System.out.printf("%s.getName()\n", getClass().getName());
return "boo2";
}
public void setName(String _name) {
System.out.printf("%s.setName(%s)\n", getClass().getName(), _name);
}
public void save() {
System.out.printf("%s.save()\n", getClass().getName());
}
}
public static void main(String[] args) {
ReflectTest rt;
try {
rt = new ReflectTest(Test.class, Test2.class);
} catch (NoSuchMethodException | SecurityException e) {
System.out.println(e);
System.exit(2);
return;
}
rt.callSequence(new Test());
rt.callSequence(new Test2());
}
}

Categories