I have class Button in package "pl.components" and I created interface IButoon in package "pl.icomponents". I would like to create a button based on this interface. This button only has methods that are in the interface.
public class Button extends com.vaadin.ui.Button implements IButton
{
public String setEnabledRole(){}
public void setToolTip(String txt) {}
}
public interface IButton
{
public void setToolTip(String txt) {}
}
So, I guess when I do
IButton ibutton = new Button();
Then my ibutton will only be able to use setToolTip. The only problem I have to do is have classNameSpaces only. Because I want to do this for a few components. And I don't want to make a few ifs that check it. So far I have done something like that:
// classNamespace - example: pl.icomponents.IButton
private Object createClass(String classNamespace, Element xml)
{
if (classNamespace.contains("pl.icomponents."))
{
int lenght = classNamespace.length();
int lenght2 = "pl.icomponents.".length();
String interfaceName = classNamespace.substring(lenght2, lenght);
String className = interfaceName.substring(1);
classNamespace = "pl.components." className;
Class<?> cls = Class.forName(classNamespace);
Object object = cls.newInstance();
Object iObject = cls.getInterfaces();
iObject = cls.newInstance();
return iObject;
}
}
Unfortunately, the method returns Object Button that has access to setEnabledRole and setToolTip.
Do you have any ideas? Maybe the "Class <?>" class has something like that? Unfortunately, I could not find anything.
CodePudding user response:
You seem to have a more fundamental misunderstanding here. What you want is possible using proxies, but this is very complicated.
Let's first explain the type system.
It is, obviously, not possible to have an instance of IButton
specifically. No interface or abstract class can be instantiated. You can only have instances of Button
here. Now, any Button
is a IButton
. In exactly the same way any instance of Schnauzer is a Dog, and is a Animal.
In java-the-language, the compiler does not let you call methods on an expression unless the compiler is sure they exist, first. In other words:
object o = "Hello";
o.toLowerCase();
does not compile, because the compiler won't let you invoke toLowerCase()
on o
. However, o
is just a variable. It isn't the object, it's a reference to it. It's like a page in an address book - the page is just a page, it's not a house - you have to follow the reference written on that page to the house first.
The point is, the object o
is referring to still has that toLowerCase
method. Objects are like treasure chests in the sand. They do not have names. new Object()
makes one. Variables are treasure maps. Object o = new Object()
does not mean o
is the treasure chest. No, o
is merely a map:
String o = new String();
Object o2 = o;
o = null;
o2 = null;
Here, in the first line, there is one treasure created and buried, and o
is a treasure map that finds it. After the second line, there are 2 treasure maps that lead to the same treasure (thus, calling the treasure o
is misleading. Also, calling the type of the treasure 'Object', because o2
is of type Object
, is simply wrong). After line 3, one of the treasure maps has been erased and is now blank. After lien 4, the other one is too. The treasure is still there; but no longer reachable. The garbage collector will eventually dig it up and toss it out.
The above exercise should make clear that IButton button = new Button();
does not magically make the button 'lose' its setEnabledRole
method. It still has it. It's just that button.setEnabledRole()
doesn't compile, but ((Button) button).setEnabledRole()
does: It's just a matter of the compiler demanding that you explicitly tell it that the treasure you find when you follow the button
treasuremap doesn't just lead to an IButton
, the thing there is specifically a Button
, and thus, setEnabledRole()
is a thing you can do.
This explains why you can't do what you want...
On to the actual solution
So what you need to do instead is create an entirely new object whose type truly does not have a setEnabledRole()
method. This completely different object will then pass on any methods it does have straight to the real button object. This pass-through object is called a proxy, and you can make them using java.lang.reflect.Proxy
:
Class<?> proxyClass = Proxy.newProxyInstance(IButton.class.getClassLoader(), new Class<?>[] {IButton.class}, (obj, method, args) -> {
// code here
});
return (IButton) proxyClass.newInstance();
is the basic setup. This creates an object that only has the methods in IButton and nothing more. Any calls to any method are all redirected to 'code here', so what you'll need to do is reflectively invoke the code in the actual object. This is complicated, but doable. I doubt you want to, though.