This reply began to be written before the reply from Virgilio Novic.
My approach has a different view, with a construction of knowledge that I consider valid to share, but the code obtained
remains more or less that of Virgilio Norvig.
This is a recursive problem (as indicated by Piovezam and Augusto Vasques in the comments). I can reduce it to a search in a directed acyclic graph.
In this graph, I have three kinds of knots:
- object node (input, does not matter for the final result)
- class nodes (no matter for the final result)
- interface nodes (interest for the final result)
The question then becomes how, from the object node, which interface nodes I can achieve. An interesting thing about the structure of this graph is the following:
- the graph is finite, always
- the input node has only one edge, which goes to a class node (identified by
getClass()
)
- the class node can point to another class node (identified by
getSuperclass()
), and can also point to several interface nodes (identified by getInterfaces()
)
- the interface node can point to several interface nodes (identified by
getInterfaces()
)
Since it is acyclic and finite, I can navigate it in depth without problems. An idea well above how I navigate this graph is as follows:
def percorre_grafo(nodo):
se nodo é objeto:
percorre_grafo(nodo.getClass())
se nodo é de classe:
para iface em nogo.getInterfaces():
percorre_grafo(iface)
se nodo.getSuperclass() != null:
percorre_grafo(nodo.getSuperclass)
se nodo é interface:
# faz algo com o nodo em questão
para iface em nogo.getInterfaces():
percorre_grafo(iface)
However, I can leave code blocks with minor concerns. For example, a block
to be the front, receiving the object in question, another to traverse the classes/superclasses of that object
and finally a last block that recursively traverses the interfaces:
def percorre_grafo__front(nodo):
# garanto aqui que nodo é do tipo "objeto"
# só tem uma aresta que aponta para um nó do tipo "classe"
percorre_grafo__classe(nodo.getClass())
def percorre_grafo__classe(nodo):
# garanto aqui que nodo é do tipo "classe"
# pode ter uma aresta que aponta para um nó do tipo "classe"
# podem ter diversas arestas que apontam para nós do tipo "interface"
se nodo.getSuperclass() != null:
percorre_grafo__classe(nodo.getSuperclass())
percorre_grafo__interface(iface) para iface em nodo.getInterfaces()
def percorre_grafo__interface(nodo):
# garanto aqui que nodo é do tipo "interface"
# podem ter diversas arestas que apontam para nós do tipo "interface"
# faz algo com o nodo em questão
percorre_grafo__interface(iface) para iface em nodo.getInterfaces()
I can still remove the recursion from the function percorre_grafo__classe
, because it is a recursive function
trivial. This is the iterative unfolding of it:
def percorre_grafo__classe(nodo):
# garanto aqui que nodo é do tipo "classe"
# pode ter uma aresta que aponta para um nó do tipo "classe"
# podem ter diversas arestas que apontam para nós do tipo "interface"
iter = nodo
enquanto iter != null:
percorre_grafo__interface(iface) para iface em iter.getInterfaces()
iter = iter.getSuperclass()
In this strategy I pass through all the nodes reachable from the object in question, including repeating the same interface several times.
If I admit that the recursion part is reach the knots, then I can pass to the recursive step the "answer" being built,
a strategy similar to the one I used in this answer to discover
the difference between the largest and smallest element of a list recursively. The do something with the node in question is
add into the set. If the node was already previously in the set, it means that I had already passed through it, then no
it is necessary to make the recursive call.
Assuming this as the strategy, and taking into account the function percorre_grafo__classe
as the point of entry
that prepares the function for recursive call, we then have the following algorithm:
def percorre_grafo__front(nodo):
# garanto aqui que nodo é do tipo "objeto"
# só tem uma aresta que aponta para um nó do tipo "classe"
percorre_grafo__classe(nodo.getClass())
def percorre_grafo__classe(nodo):
# garanto aqui que nodo é do tipo "classe"
# pode ter uma aresta que aponta para um nó do tipo "classe"
# podem ter diversas arestas que apontam para nós do tipo "interface"
conjunto_interfaces = new Set()
iter = nodo
enquanto iter != null:
percorre_grafo__interface(iface, conjunto_interfaces) para iface em iter.getInterfaces()
iter = iter.getSuperclass()
def percorre_grafo__interface(nodo, conjunto_interfaces):
# garanto aqui que nodo é do tipo "interface"
# podem ter diversas arestas que apontam para nós do tipo "interface"
se conjunto_interfaces não contém nodo:
conjunto_interfaces.add(nodo)
percorre_grafo__interface(iface) para iface em nodo.getInterfaces()
And in Java, what would that be like?
HashSet<Class<?>> percorre_grafo__front(Object nodo) {
// garanto aqui que nodo é do tipo "objeto"
// só tem uma aresta que aponta para um nó do tipo "classe"
return percorre_grafo__classe(nodo.getClass());
}
HashSet<Class<?>> percorre_grafo__classe(Class<?> nodo) {
// garanto aqui que nodo é do tipo "classe"
// pode ter uma aresta que aponta para um nó do tipo "classe"
// podem ter diversas arestas que apontam para nós do tipo "interface"
HashSet<Class<?>> conjunto_interfaces = new HashSet<>();
Class<?> iter = nodo;
while (iter != null) {
Stream.of(iter.getInterfaces()).forEach(iface -> percorre_grafo__interface(iface, conjunto_interfaces));
iter = iter.getSuperclass();
}
return conjunto_interfaces;
}
void percorre_grafo__interface(Class<?> nodo, HashSet<Class<?>> conjunto_interfaces) {
// garanto aqui que nodo é do tipo "interface"
// podem ter diversas arestas que apontam para nós do tipo "interface"
if (conjunto_interfaces.add(nodo)) {
Stream.of(nodo.getInterfaces()).forEach(iface -> percorre_grafo__interface(iface, conjunto_interfaces));
}
}
Using a standard name/code a little more Java:
public HashSet<Class<?>> percorreGrafo(Object obj) {
// garanto aqui que nodo é do tipo "objeto"
// só tem uma aresta que aponta para um nó do tipo "classe"
return percorreGrafo__classe(obj.getClass());
}
private HashSet<Class<?>> percorreGrafo__classe(Class<?> clazz) {
// garanto aqui que nodo é do tipo "classe"
// pode ter uma aresta que aponta para um nó do tipo "classe"
// podem ter diversas arestas que apontam para nós do tipo "interface"
HashSet<Class<?>> interfaces = new HashSet<>();
for (Class<?> iter = clazz; iter != null; iter = iter.getSuperclass()) {
Stream.of(iter.getInterfaces()).forEach(iface -> percorreGrafo__interface(iface, interfaces));
}
return interfaces;
}
private void percorreGrafo__interface(Class<?> clazz, HashSet<Class<?>> interfaces) {
// garanto aqui que nodo é do tipo "interface"
// podem ter diversas arestas que apontam para nós do tipo "interface"
if (interfaces.add(clazz)) {
Stream.of(clazz.getInterfaces()).forEach(iface -> percorreGrafo__interface(iface, interfaces));
}
}
Removing Stream
:
public HashSet<Class<?>> percorreGrafo(Object obj) {
// garanto aqui que nodo é do tipo "objeto"
// só tem uma aresta que aponta para um nó do tipo "classe"
return percorreGrafo__classe(obj.getClass());
}
private HashSet<Class<?>> percorreGrafo__classe(Class<?> clazz) {
// garanto aqui que nodo é do tipo "classe"
// pode ter uma aresta que aponta para um nó do tipo "classe"
// podem ter diversas arestas que apontam para nós do tipo "interface"
HashSet<Class<?>> interfaces = new HashSet<>();
for (Class<?> iter = clazz; iter != null; iter = iter.getSuperclass()) {
for (Class<?> iface: iter.getInterfaces()) {
percorreGrafo__interface(iface, interfaces);
}
}
return interfaces;
}
private void percorreGrafo__interface(Class<?> clazz, HashSet<Class<?>> interfaces) {
// garanto aqui que nodo é do tipo "interface"
// podem ter diversas arestas que apontam para nós do tipo "interface"
if (interfaces.add(clazz)) {
for (Class<?> iface: clazz.getInterfaces()) {
percorreGrafo__interface(iface, interfaces);
}
}
}
Note that the method Set.add(E e)
returns true if you don’t have the element yet e
, and
on top of the added set. Case e
already exists, returns false. So, add
a node in the set of visited interfaces is already doing at the same time the verification
that the element is in the set. So, if interfaces.add(clazz)
return true
, that
means that clazz
was not yet in the set and that was added.
How can I be so sure of the construction of these graphs? Well, reading the documentation.
First of all, the language doesn’t make multiple inheritance (grammar described in JLS 8.1.4). Therefore, if I am inheriting from some class (ps: always is), I cannot inherit from any other. Therefore, only possessing at most one superclass.
It is documented in Java that every class inherits from Object
, directly or indirectly (JLS 4.3.2). And it’s also documented that Object.class.getSuperclass()
returns null
(Javadoc).
It also has in the Java language specification that an interface cannot implement itself cyclically (JLS 12.2.1). Therefore, it is impossible to exit an interface and through getInterfaces()
, get into it again. I at all times I will arrive at an interface that does not extend any other, an interface that is end of line.
Note that the Virgilio Novic code does the same thing as this answer, but in one method, and returns the appropriate set of interfaces (although it happens to pass through one of these interfaces more than once).
Who gave the negative could explain to me how I can improve the question? To the point of being worthy of its positive if possible =3
– Jefferson Quesado
I may be traveling, but I can’t go around the superclasses
Object
and taking the interfaces implemented by each one?– Piovezan
I think the reflection should be done recursively for each interface obtained directly. ps: pressed +1, the question is good.
– Augusto Vasques
@Augustovasques, thank you very much =D Taking advantage (cc Piovezan), I managed to arrive at something that I believe to be the answer, but I am doing more tests and with laziness of a formal demonstration based in that reply of mine. The answer has nothing to do with Java or reflection, but about recursion, according to your tip 2
– Jefferson Quesado