3

Q: Why does the name of the containing class appear twice?

Context: I'm generating code and the goal is to get the declaration of the field, as written in the source (fully qualified is fine, but I need the type parameter): test.Foo.Bar<java.lang.String>

package test;

import java.lang.reflect.Field;
import java.lang.reflect.Type;

public class Foo
{
  public static class Bar<TYPE> {}

  private Bar<String> bar;

  public static void main(String[] args) throws Exception
  {
    Field field = Foo.class.getDeclaredField("bar");
    Type genericType = field.getGenericType();
    Type type = field.getType();

    System.out.println("genericType: " + genericType.getTypeName());
    System.out.println("       type: " + type.getTypeName());
  }
}

Output:

genericType: test.Foo.test.Foo$Bar<java.lang.String>
       type: test.Foo$Bar

UPDATE: Thank you everyone for your input. Since this question was marked as duplicate, I posted my current working solution over there.

Reto Höhener
  • 5,419
  • 4
  • 39
  • 79
  • 2
    Also see https://bugs.openjdk.java.net/browse/JDK-8054213. – Radiodef Jul 13 '17 at 14:32
  • @Radiodef good catch, that issue seems to be identical to my question. So it is a bug? (convert to answer) – Reto Höhener Jul 13 '17 at 14:35
  • 1
    It probably should be considered a bug, but there isn't really a standardized specification for `String` representations of types. [`getTypeName`](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Type.html#getTypeName--) doesn't specify any particular requirements and neither does `toString`. It's not a very well-developed section of the API. – Radiodef Jul 14 '17 at 01:40

1 Answers1

3

genericType is instance of ParameterizedTypeImpl

the toString() method returns ownerType class' name and then rawType classname.

The decompiled piece of the code

public String toString() {
    StringBuilder var1 = new StringBuilder();
    if(this.ownerType != null) {
        if(this.ownerType instanceof Class) {
            var1.append(((Class)this.ownerType).getName()); //<---
        } else {
            var1.append(this.ownerType.toString());
        }

        var1.append(".");
        if(this.ownerType instanceof ParameterizedTypeImpl) {
            var1.append(this.rawType.getName().replace(((ParameterizedTypeImpl)this.ownerType).rawType.getName() + "$", ""));
        } else {
            var1.append(this.rawType.getName()); //<------------
        }
StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • 1
    Thank you for investigating. I'm still wondering why it is like that. How is this useful? How can I achieve my goal? – Reto Höhener Jul 13 '17 at 14:29
  • 1
    They try to provide more info - for your question you can check and skip ` System.out.println("raw type from generic: " + ((ParameterizedTypeImpl)genericType).getRawType());` – StanislavL Jul 13 '17 at 14:33
  • ParameterizedTypeImpl is a sun class, I would prefer not to use it. – Reto Höhener Jul 13 '17 at 14:36
  • Type is an Interface and implementation could be platform dependent or VM dependent. – StanislavL Jul 13 '17 at 14:39
  • You can try to subtract `field.getDeclaringClass().getName()` from the `genericType.getTypeName()` but again actual toString() is based on VM dependent implementation – StanislavL Jul 13 '17 at 14:42
  • You are right, and "returns a string describing this type" is not really a very strict contract... Leaves me wondering how people do it. Going the hackish way for now. – Reto Höhener Jul 13 '17 at 14:42
  • 1
    @Zalumon You don't need `ParameterizedTypeImpl`, just use its JDK interface `ParameterizedType`. – Sotirios Delimanolis Jul 13 '17 at 14:44
  • @SotiriosDelimanolis thanks for the pointer, I used it in my implementation, as posted in the update of the question. – Reto Höhener Jul 13 '17 at 15:44