Imagine that I have two Class A and B, B extends A, like
class B extends A
{
....
}
However, in my case, Class A is encrypted and can be only loaded by my ClassLoader at runtime (at compiling time, A.class can not be recognized as a .class file because it is encrypted). This means Class A doesn't exist at compiling time.
My questions are:
how can write the code for Class B, as some methods override the methods in Class A?
how can I specify Class B extends to Class A at runtime?
You can't with reflection. But you can with CGLIB and perhaps javassist
You can create a dummy copy of A which has all the methods you want to override and compile and deploy just B.
If you don't know what methods you want to override until runtime, you will need to generate code using either the Compiler API, however a library like Objectweb's ASM is likely to be much simpler. I prefer ASM because it can easily generate code to generate what you want from a template. i.e. you don't have to write all the code yourself.
Related
Imagine we have some class A.java. It uses B.java in some way, i.e. imports it, calls its methods, uses its attributes etc. We compile the files.
Now I would like to get a deeper understanding of what we have got.
Does A.class stores some information about B inside itself? What information - just name, method which is called, final variables which are used?
If there is not that much information about B in A.class after compilation - why cant we compile A without B.class at all?
If we replace B.class in our bundle with B.class which is another one - when it works and when not?
If A.class runs ok with new B.class, may A.class use some information from OLD B.class which was incorporated into A.class during its compilation? I.e. may we finally have a mixed logic of B.class in our project?
Basing on the answers on questions above: can we somehow compile class which depends on other classes without knowing their exact implementation at compile time, and provide the dependency only in runtime?
The "import" keyword doesn't actually import anything. Basically it's just a way to refer to the full path of a class by its class name. So when we say
import java.lang.String;
then we can refer to that class just by its name "String" in our code. At compilation time, anywhere we have class "String" will be be replaced by "java.lang.String". That's it.
This is also why if you ever have more than one class with the same name but in different packages, you can import at most only one of them. The other class you must refer to by its full qualified name.
Onto the questions:
1. Does A.class stores some information about B inside itself? just name, method which is called, final variables which are used? If there is not that much information about B in A.class after compilation - why cant we compile A without B.class at all?
Not really. Class A will only use the fully qualified class names and method signatures when you invoke other classes. Using our String example, calling
mystring.charAt(0)
will compile into byte code looking something like this:
aload_1 [myString]
iconst_0
invokevirtual java.lang.String.charAt(int)
No inner state of the other classes is stored inside our Class A except possibly constants that are inlined. So be very careful about making fields public final if the value may change in the future. (see below for work around)
We need B.class when we compile Class A so the compiler can make sure class B has those methods we want to use.
2. If we replace B.class in our bundle with B.class which is another one - when it works and when not?
the new B.class will work if the full qualified name (package etc) is identical and it has the correct method signatures we are trying to use. The new class B can have new methods added and can even have different implementations of the methods we use. However, it must not modify the method signature of the methods we use. If the old method no longer exists or its signature is different, we will get an java.lang.LinkageError at compile time.
3. If A.class runs ok with new B.class, may A.class use some information from OLD B.class which was incorporated into A.class during its compilation? I.e. may we finally have a mixed logic of B.class in our project?
The only issue might be inlined constants from the old B. This is why the Java Coding Guidelines Item 31 (page 115) says "Do not apply public final to constants whose value might chage in later releases".
Instead, make a getter method:
ex:
class BadFoo{
//bad, VERSION could be inlined
//and later when we change the version, other classes will have the old value!
public static final int VERSION =1;
}
class BetterFoo{
private static int version =1;
//outside classes must call method getVersion()
//field version can not be inlined at compile time.
//JIT may inline at runtime which is OK
public static final int getVersion(){
return version;
}
}
4. Basing on the answers on questions above: can we somehow compile class which depends on other classes without knowing their exact implementation at compile time, and provide the dependency only in runtime?
Yes, code to interfaces.
the interface should contain all the methods that you need to call. Our Class A should only refer to the caller by the interface. If the object instance we want to call is passed in, then Class A doesn't know (or need to know) what the actual type is AND doesn't need to know about it at compile time.
This is one of the main purposes and advantages of Dependency Injection
Even aside from Dependency Injection, coding to interfaces has advantages. For example, if we use Map instead of HashMap, we can later change the code to use a different Map implementation such as ConcurrentHashMap by only changing one place in our code. The rest of our code will just work since it only knows it's a Map.
This is addressed by Chapter 13. Binary Compatibility of the Java Language Specification.
As this may be more specific than you want, I'll give a brief summary:
If class A uses a feature f of class B, the class file of A must contain a symbolic reference to f. When class A is loaded, but B does not provide f, loading class A fails with an java.lang.LinkageError, which prevents A from being used.
An exception are compile time constant expressions, which the compiler must inline, i.e. the symbolic references must be replaced by their value at compile time, and class A will continue to use the original values even if class B has been changed.
Another exception is that adding or removing annotations has no effect on the correct linkage of the binary representations of programs.
No other information about B is incorporated into A at compile time, and everything else about B may be changed without affecting its use by an already compiled class A.
Assume I find something lacking in the default base class Object, and write a class of my own to use instead.
class MyObject extends Object
{
...
}
Is there a way to tell the Java compiler to use my new class as base class instead of the Object class?
Thus avoiding
class MyClass extends MyObject
and just go
class MyClass
and have it implicitly extend MyObject using, say a command line parameter to the compile
javac MyClass --defaultBase=MyObject
That would mean that all Jars and pre-compiled Class-files would use Object, but anything I compile myself would use my own class, except of course, my new base class. (I wouldn't want a circular dependency, would I.)
You can change the Object class within many limitations, but that not really a good idea. The best solution is to create your own class you want other classes to extend.
If you want to extend Object, the best option is to create a Utility method which takes an Object as an argument.
Is there a way to tell the Java compiler to use my new class as base class instead of the Object class?
No. You can't.
The Java Language Specification (JLS 4.3.2) states that the java.lang.Object class is a superclass of all other classes. If a compiler didn't implement that, it wouldn't be a compliant Java compiler. At any rate, I know of no Java compiler ("compliant" or not) that does allow you to do this.
#Peter Lawrey mentions that it is possible to change the Object class. This is true ... if you know what you are doing. But there are dependencies on internal details of the Object class hard-wired into other class ... and the native code implementation of the JVM. If you change some things (for example, adding instance fields or changing method signatures) you are liable to break the JVM, tools in the Java tool-chain, and/or 3rd-party libraries and tools (like your favourite IDE!). The breakage is liable to be castrophic, and difficult to diagnose.
On top of that, if your change doesn't break Java, you are still stuck with the problem that a real JVM won't run your code. That makes monkeying with Object a viable proposition for "research purposes only" ... at best.
For the record, it is technically possible to override the core classes of the the Java runtime library using the -Xbootclasspath VM option(s) (type java -X to get the info). However, this is not intended to be done by a usual developer (see other answers to this question).
You can try it yourself by modifying and compiling the source of the java.lang.Object to a directory named core-classes, for example, and passing the -Xbootclasspath/p:core-classes as a JVM argument.
Assume I obtained a Class object by loading a class dynamically using Class.forName("Foo"). Foo is abstract in this case, so I cannot call newInstance() on it. (How) is it possible to declare a concrete subclass of Foo?
You declare it as not abstract e.g.
class Bar extends Foo {
// implement methods are required.
}
In short, just because you loaded the class dynamically doesn't mean the rules change ;)
Foo is not available at compile-time
If you don't have Foo available at compile time, you still need to have a sub-class of this class which is concrete to create it. To do this you have two options.
find a concrete class and create that. You can use the Reflections library to find such a class.
generate code and compile it using the Compiler API, or BeanShell.
generate byte code for the subclass using ObjectWeb's ASM.
I suspect the first option is what you need because unless there is a subclass provided for you, you still need to generate the code for the abstract methods.
I have abstract methods in a class that need to be implemented by a foreign class in a SEPARATE project that uses my project.
-- All classes instanceof A are initially generated using reflection --
So anyway, say Class A is abstract, and Class B (non-abstract) extends A
B has all the unimplemented methods in Class A because B is in my workspace so I know to add those methods.
C also extends A, but C only has a subset of the abstract methods in A. C, however, is not in my workspace.
Therefore, for each abstract method in C NOT in A, I need to find some way to add the method for A like so:
(For each method)
public <corresponding return type> <missingMethodName>() { return null; }
Is this possible?
P.S. Please assume that I either have to completely rewrite my code to be in sync with the objects I have no control over, or implement a solution like the one I am alluding to above.
No, unless I'm reading you incorrectly, what you're asking for doesn't really make much sense.
If you wanted to inject a method
public <corresponding return type> <missingMethodName>() { super.<missingMethodName>(); }
into C, which extends A, which doesn't implement that method, what would it exactly do?
If you want to provide a default implementation in A, that's fine, and it won't affect C. If you add abstract methods into A, C must implement them, mark itself as abstract, or it won't compile (or throw serialization, or some weird error) if you run with a C compiled with an older A.
You should never need to do this as any instance method which has a super implementation can be called on a sub-class instance.
You can add these methods using byte code, but the only difference they would make is to change the list of getDefinedMethods(). However it wouldn't change the behaviour of the objects of the class.
its quiet difficult but you can do it with Javassist
Javassist (Java programming assistant) is a Java library providing a means to manipulate the Java bytecode of an application.1 In this sense Javassist provides the support for structural reflection, i.e. the ability to change the implementation of a class at run time.
Bytecode manipulation is performed at load-time through a provided class loader.
http://en.wikipedia.org/wiki/Javassist
It is possible in plain Java to override a method of a class
programmatically at runtime (or even create a new method)?
I want to be able to do this even if I don't know the classes at compile time.
What I mean exactly by overriding at runtime:
abstract class MyClass{
public void myMethod();
}
class Overrider extends MyClass{
#Override
public void myMethod(){}
}
class Injector{
public static void myMethod(){ // STATIC !!!
// do actual stuff
}
}
// some magic code goes here
Overrider altered = doMagic(
MyClass.class, Overrider.class, Injector.class);
Now, this invocation...
altered.myMethod();
...would call Injector.myMethod() instead of Overrider.myMethod().
Injector.myMethod() is static, because, after doing "magic"
it is invoked from different class instance (it's the Overrider),
(so we prevent it from accessing local fields).
You can use something like cglib for generating code on-the-fly
In java6 has been added the possibility to transform any already loaded class. Take a look at the changes in the java.lang.instrument package
For interfaces there is java.lang.reflect.Proxy.
For classes you'll either need a third-party library or write a fair bit of code. Generally dynamically creating classes in this way is to create mocks for testing.
There is also the instrumentation API that allows modification of classes. You can also modify classes with a custom class loader or just the class files on disk.
I wrote an article for java.net about how to transparently add logging statements to a class when it is loaded by the classloader using a java agent.
It uses the Javassist library to manipulate the byte code, including using the Javassist compiler to generate extra bytecode which is then inserted in the appropriate place, and then the resulting class is provided to the classloader.
A refined version is available with the slf4j project.
If I got it right, the main problem that concerns you is how to pass a static method delegate (like in C#), through the instance interface method.
You can check this article: A Java Programmer Looks at C# Delegates (archived), which shows you how to get a reference to your static method and invoke it. You can then create a wrapper class which accepts the static method name in its constructor, and implements your base class to invoke the static method from the instance method.