I decompiled Java (actually Dalvik) bytecode. In the beginning of a method, I access a field of an instance member directly (i.e. not through a getter).
It seems tha Java calls Object.getClass()
on the accessed instance member (mOther
), but doesn't use the result anywhere. Is this some sort of check? Why is this call needed? I suspect it is because I access a field directly (which is defined in that class), but I don't see the connection.
The Java code and the decompiled bytecode are as follows.
(Note that the last instruction loads lifeTime
as constant 0x0001
because in MyOtherClass
, I have lifeTime
as a public final
field, and is currently initialized from code.)
MyOtherClass other = mOther;
if (mAge >= other.lifeTime) { // lifeTime is initialized to 0x0001
end();
return;
}
.line 53
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/engine/MyClass1;->mOther:Lcom/example/engine/MyOtherClass;
move-object/from16 v16, v0
.line 54
.local v16, other:Lcom/example/engine/MyOtherClass;
move-object/from16 v0, p0
iget v0, v0, Lcom/example/engine/MyClass1;->mAge:I
move/from16 v18, v0
// Why is Object->getClass() called?
invoke-virtual/range {v16 .. v16}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
const/16 v19, 0x0001
It was requested in comments that I provide the method's full source code. Note that mOther
is a final field (for performance reasons). Here you're:
@Override
public void doStep() {
MyOtherClass other = mOther;
if (mAge >= other.lifeTime) {
end();
return;
}
mAge += TICK_TIME;
boolean isSurrounded = false;
if (mAge > mLastSurroundTime + other.surroundingTime) {
int distance = (int)other.maxSurroundDistance;
for (int bx = bx0; bx <= bx1; ++bx) {
if (bx < 0 || bx >= mSize) { continue; }
for (int by = by0; by <= by1; ++by) {
if (by < 0 || by >= mSize) { continue; }
ArrayList<WorldObject> candidates = getCandidatesAtPos(bx, by);
for (int i = 0; i < candidates.size(); ++i) {
WorldObject obj = candidates.get(i);
if (mSelf!= obj && mSelf.getDistanceFrom(obj) <= other.maxSurroundDistance) {
obj.notifyDangerImminent(mSelf);
isSurrounded = true;
}
}
}
}
if (isSurrounded) { mLastSurroundTime = mAge; }
}
}
I'm assuming lifeTime is a final field that is assigned upon declaration:
final int lifeTime = 0x0001;
If so, the bytecode is optimized in the following way (it has next to nothing to do with the VM, pure compiler magic):
A simpler example:
class Test {
private final int myFinalField = 1;
int test(Test t) {
return t.myFinalField;
}
}
If we look at the byte codes of the test() method (JVM this time, but should you translate it to Dalvik, it will be essentially the same), here is a call to getClass() too:
// access flags 0x0
test(LTest;)I
L0
LINENUMBER 5 L0
// load t
ALOAD 1
// if (t == null) throw new NullPointerException(); compressed in only two instructions
INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
POP
// the actual value of myFinalField
ICONST_1
IRETURN
L1
LOCALVARIABLE this LTest; L0 L1 0
LOCALVARIABLE t LTest; L0 L1 1
MAXSTACK = 1
MAXLOCALS = 2