1

I'm looking at some decompiled code and seeing .getClass();, i.e. nothing being done with its return value.

  public String forLocale(Locale locale, String keyword) {
    Stream var10000 = getLocaleMappingList(locale, this.getSupportedLocales());
    Map var10001 = this.translations;
    var10001.getClass();
    Map<String, String> translation = (Map)var10000.map(var10001::get).filter((m) -> {
      return m.containsKey(keyword);
    }).findFirst().orElse(this.translations.get(FALLBACK));
    Preconditions.checkState(translation.containsKey(keyword), keyword + " is not a valid translation key");
    return (String)translation.get(keyword);
  }

What is that for? Was it in the original code like that? (So far I haven't seen an instance of decompiled code not matching up at least line-wise to source code.)

It kind of looks like an assertion, but then what is achieved by doing that as opposed to letting things go wrong at var10001::get? Or is it more about performance?


Update

Here's the bytecode. Cool thing to learn how to do!

 // access flags 0x1
  public forLocale(Ljava/util/Locale;Ljava/lang/String;)Ljava/lang/String;
   L0
    LINENUMBER 184 L0
    ALOAD 1
    ALOAD 0
    INVOKEVIRTUAL com/spotify/i18n/Translations.getSupportedLocales ()Ljava/util/Set;
    INVOKESTATIC com/spotify/i18n/Translations.getLocaleMappingList (Ljava/util/Locale;Ljava/util/Collection;)Ljava/util/stream/Stream;
    ALOAD 0
    GETFIELD com/spotify/i18n/Translations.translations : Ljava/util/Map;
    DUP
    INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
    POP
    INVOKEDYNAMIC apply(Ljava/util/Map;)Ljava/util/function/Function; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      (Ljava/lang/Object;)Ljava/lang/Object;, 
      // handle kind 0x9 : INVOKEINTERFACE
      java/util/Map.get(Ljava/lang/Object;)Ljava/lang/Object; itf, 
      (Ljava/util/Locale;)Ljava/util/Map;
    ]
Andrew Cheong
  • 29,362
  • 15
  • 90
  • 145
  • 2
    check byte code using javap or try different decompiler. It might be just incorrectly decompiled. – rkosegi Mar 18 '21 at 08:00
  • Also, which decompiler are you using and have you tried a different one / a newer version of yours? JD-core, JFR, Fernflower, Procyon are all decompilers that I know have been trying to keep up with the recently fast Java evolution. – Petr Janeček Mar 18 '21 at 08:01
  • @GhostCat - Oh, well honestly no. Why would I withhold information on purpose? (It's closed source so I didn't think the rest of the class details would help, and I thought it seemed odd / possibly idiomatic enough that someone would recognize its intent right away.) – Andrew Cheong Mar 18 '21 at 08:07
  • @GhostCat - Ah, I see. Yea no, I see now that it matters more than I thought. But honestly how does one know _a priori_ everything that matters, heh. I included classloader wondering if this line is trying to trigger a NoClassDefFound or ClassNotFound on purpose. I don't know if that even makes sense though, I'm new to Java. – Andrew Cheong Mar 18 '21 at 08:19
  • Ah, found it. I'm using the Java Bytecode Decompiler that comes bundled with IntelliJ IDEA. – Andrew Cheong Mar 18 '21 at 08:39

1 Answers1

5

This looks like decompiled code, and my guess is that the decompiler hasn't generated Java code that is equivalent to the original source code.

The literal meaning of

var10001.getClass();

is to return the Class object for the type of the object that var10001 refers to. But the value that is returned appears to be discarded, so the call (apparently) doesn't achieve anything. Hence, my tentative conclusion that the decompiler has stuffed up.

You may need to read the (disassembled) bytecodes directly to discern what they are actually doing. (Or you could try a different decompiler.)


UPDATE

It is plausible that getClass() is called solely for the side-effect of checking for null. (I've never seen that idiom ... but it would work.) I wouldn't expect it to make the code faster, but it would make it more compact.

However, if this is being done in the (original) source code, it would appear to be unnecessary. A couple of lines later, the code takes var10001::get and passes it as an argument in a Stream.map call. I'm pretty sure that that evaluating var10001::get will entail checking that var10001 is not null.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Or maybe the original source has `someVariable.getClass();` for magical purposes. – ernest_k Mar 18 '21 at 08:05
  • Well yes ... but I can't think what that magical purpose might be. Either way, we are working from incomplete information here, so a definitive answer is not possible. – Stephen C Mar 18 '21 at 08:07
  • @StephenC - Hi, thanks. I edited my post with bytecode. But I also found this: https://stackoverflow.com/questions/13115812/why-bytecode-calls-object-getclass-at-a-direct-field-access - If it matches up, maybe it's a performance optimization after all. – Andrew Cheong Mar 18 '21 at 08:54
  • 2
    The source code artifact `var10001::get` implies a `null` check, but there is no automatic check at bytecode level or, in other words, the expression gets compiled to a null check and an `invokedynamic` instruction whereas apparently, the decompiler converted the `invokedynamic` instruction back to a method reference but did not remove the associated null check. – Holger Mar 18 '21 at 09:14