3

I have strange behavior of the static final values in my interface:

public interface IDictionaryErrorTypes {

    final static int ERROR_UNKNOWN      = 0;
    final static int ERROR_XML      = 1;
    final static int ERROR_CONNECTION   = 3;

}

public interface IDictionaryListModel extends IDictionaryErrorTypes,
                                                    //... and other interfaces
{

}

public class DictionaryListModel implements IDictionaryListModel {

// ... some code ...

private int getErrorCode(Exception error) {
    // determinate the error code
    if (error instanceof ParserConfigurationException
            || error instanceof SAXException
            || error instanceof SAXParseException) {
        return ERROR_XML;
    } else if (error instanceof UnknownHostException
            || error instanceof MalformedURLException
            || error instanceof IOException) {
        return ERROR_CONNECTION;
    } 
    return ERROR_UNKNOWN;
}

And now when I run the application ERROR_XML, ERROR_CONNECTION and ERROR_UNKNOWN values equals ZERO - 0. It seems to me very strange. Please look at attached image

java static fields interface bug

If I use in my model IDictionaryErrorTypes.ERROR_CONNECTION it still has the same behavior. But if I remove the "implements IDictionaryErrorTypes" and then use IDictionaryErrorTypes.ERROR_CONNECTION it works - constant values are exactly as they sound be.

When I use class instead of interface it also works fine.

Can someone explain this behavior? (p.s. Im using android platform)

p.s.(2) I also experimented by adding/removing "final", "static", "public" keywords ( That is why interface declaration in the image and code about a little bit differ). But behavior is the same

smali code of the WORKING VERSION (using class instead of interface):

.method private getErrorCode(Ljava/lang/Exception;)I
    .registers 3
    .parameter "error"

    .prologue
    .line 145
    instance-of v0, p1, Ljavax/xml/parsers/ParserConfigurationException;

    if-nez v0, :cond_c

    .line 146
    instance-of v0, p1, Lorg/xml/sax/SAXException;

    if-nez v0, :cond_c

    .line 147
    instance-of v0, p1, Lorg/xml/sax/SAXParseException;

    if-eqz v0, :cond_e

    .line 148
    :cond_c
    const/4 v0, 0x1

    .line 154
    :goto_d
    return v0

    .line 149
    :cond_e
    instance-of v0, p1, Ljava/net/UnknownHostException;

    if-nez v0, :cond_1a

    .line 150
    instance-of v0, p1, Ljava/net/MalformedURLException;

    if-nez v0, :cond_1a

    .line 151
    instance-of v0, p1, Ljava/io/IOException;

    if-eqz v0, :cond_1c

    .line 152
    :cond_1a
    const/4 v0, 0x3

    goto :goto_d

    .line 154
    :cond_1c
    const/4 v0, 0x0

    goto :goto_d
.end method

// class instead of interface

.class public Ltj/zar/projects/kathtranslator/interfaces/common/IDictionaryErrorTypes;
.super Ljava/lang/Object;
.source "IDictionaryErrorTypes.java"


# static fields
.field public static final ERROR_CONNECTION:I = 0x3

.field public static final ERROR_UNKNOWN:I = 0x0

.field public static final ERROR_XML:I = 0x1


# direct methods
.method public constructor <init>()V
    .registers 1

    .prologue
    .line 3
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

smali code of the UNWORKING VERSION:

.method private getErrorCode(Ljava/lang/Exception;)I
    .registers 3
.parameter "error"

.prologue
.line 145
instance-of v0, p1, Ljavax/xml/parsers/ParserConfigurationException;

if-nez v0, :cond_c

.line 146
instance-of v0, p1, Lorg/xml/sax/SAXException;

if-nez v0, :cond_c

.line 147
instance-of v0, p1, Lorg/xml/sax/SAXParseException;

if-eqz v0, :cond_e

.line 148
:cond_c
const/4 v0, 0x1

.line 154
:goto_d
return v0

.line 149
:cond_e
instance-of v0, p1, Ljava/net/UnknownHostException;

if-nez v0, :cond_1a

.line 150
instance-of v0, p1, Ljava/net/MalformedURLException;

if-nez v0, :cond_1a

.line 151
instance-of v0, p1, Ljava/io/IOException;

if-eqz v0, :cond_1c

.line 152
:cond_1a
const/4 v0, 0x3

goto :goto_d

.line 154
:cond_1c
const/4 v0, 0x0

goto :goto_d
.end method

// interface

.class public interface abstract Ltj/zar/projects/kathtranslator/interfaces/common/IDictionaryErrorTypes;
.super Ljava/lang/Object;
.source "IDictionaryErrorTypes.java"


# static fields
.field public static final ERROR_CONNECTION:I = 0x3

.field public static final ERROR_UNKNOWN:I = 0x0

.field public static final ERROR_XML:I = 0x1

solve

Ive cleaned up the project, changed emulator to real device and wrote some test.

Results

It seems a debugger problem:

As you can see in the image error is "3" and ERROR_CONNECTION is "0". And device actually runs the next string (green string), that means that debugger thinking that 3 == 0, but actually its 3 == 3

Conclusion

Debugger bug.

enter image description here

pleerock
  • 18,322
  • 16
  • 103
  • 128
  • See: http://stackoverflow.com/questions/320588/interfaces-with-static-fields-in-java-for-sharing-constants?answertab=active#tab-top – WilHall Oct 15 '12 at 01:29
  • I am aware that this doesn't necessarily explain the strange behavior. Though, it probably shouldn't be done anyway. – WilHall Oct 15 '12 at 01:30
  • `final`, `static`, and `public` are all default for constants in interfaces, no need to have ever specified them at all. I'm more concerned about the purpose here. In general it is a really bad idea to lose error information, and to compress multiple error types into one. I would stick with the original messages from the original exceptions as far as possible. – user207421 Oct 15 '12 at 01:40
  • @EJP you mean fields, not constants? – om-nom-nom Oct 15 '12 at 01:41
  • thanks EJP, I knew this. But the error is kind of weird, so Ive tried to make this weird actions – pleerock Oct 15 '12 at 01:58
  • Pleerock: can you run baksmali on your apk, and post the smali source for the getErrorCode() method? – JesusFreke Oct 15 '12 at 02:06
  • Also, what compiler are you using? Sun java 6? Openjdk 6? – JesusFreke Oct 15 '12 at 02:17
  • @JesusFreke, Ive updated the question. Im using oracle jdk6 – pleerock Oct 15 '12 at 02:30
  • Based on that bytecode, the method looks correct. It will only return 0 for cases when the exception doesn't match one of the ones you mention. The static fields aren't being accessed at all - the static final values are being compiled in directly. (the "const v0, 0xnn" instructions) – JesusFreke Oct 15 '12 at 02:33
  • oh, no, sorry its the version where I use IDictionaryErrorTypes.Constant without implementing interface. Give me a minute will build another apk – pleerock Oct 15 '12 at 02:38
  • The working and non-working versions are identical – JesusFreke Oct 15 '12 at 02:45
  • Yes, I see. But Ive complied different versions, Ive check it again, tried to compile again and its the same. Can you explain this? – pleerock Oct 15 '12 at 02:49
  • Ive also appended the interfaces smali code of both versions if you are interested in – pleerock Oct 15 '12 at 02:54

2 Answers2

1

Either whatever debugger you're using is trolling you (most likely answer) or this is a bug in the android SDK you are using. Did you think to look at what is actually returned by that method?

Also note, no matter what you do, a field in an interface is always static final and public by default - the compiler will make it so.

public interface MyInterface
{
    public static final int TEST = 3;
}

public interface MyInterface2 extends MyInterface
{
    public final static int TEST2 = 5;
}

public class InterfaceTest implements MyInterface2
{
    public void printInterfaceConstants()
    {
        System.out.println(TEST);
        System.out.println(TEST2);
    }

    public static void main(String[] args)
    {
        InterfaceTest it = new InterfaceTest();
        it.printInterfaceConstants();
    }
}

Output:

3
5

And as has been mentioned, don't do this in the first place. Implementation details shouldn't be in interfaces.

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • 1
    Ha, you beat me. I did almost exactly the same thing. My test program prints the proper values when run locally, using normal java, as well as when converted to dex and run on a phone. It sounds like a case of debugger trolling :) – JesusFreke Oct 15 '12 at 01:49
  • I also did the same thing, Ive created simple application that emulates the same behavior and it works fine. Im not a totally noobie, but I dont know why it works so in case above. The project is a little bit huge so I dont even know what part of system I should strip to find an error. And about ACTUALLY returned value - its zero. Ive checked it too. Maybe its android bug, so Ive put dalvik tag on this question – pleerock Oct 15 '12 at 01:56
  • You say something about removing "implements ERROR_CONNECTION". What is that? It sounds like you have something very odd going on. The compiler isn't going to change behaviors just because the [SSCCE](http://sscce.org/) is ... an SSCCE. – Brian Roach Oct 15 '12 at 02:10
  • Its worth noting that I disassembled my little test program, and the values of these static final fields are being compiled in -- it's not actually accessing the fields at all. But when I modify it to actually read the values from the static fields, it is still getting the correct values. – JesusFreke Oct 15 '12 at 02:16
  • @Brian Roach, sorry it was typo – pleerock Oct 15 '12 at 02:33
1

I think that the only possible explanation is that you have something broken in you edit / build / run / debug procedures, and you are actually running a different version of the code than you think you are.

The evidence for this is that you have decompiled the two versions of the class that you believe that you are running, and the decompiled code clearly shows that:

  1. it is using inlined constant values, and
  2. the constant values are the ones that you expect.

But when you execute the code (supposedly) it clearly behaves differently to what those bytecodes say.

I can only see one plausible explanation: you are not executing those bytecodes. Instead, you are executing a different version of the bytecodes. In short, you are not recompiling / rebuilding / redeploying everything that needs to be ... done.

The debugger output is interesting because it tends to confirm this hypothesis, in that it seems to say that your executable has a copy of the interface with constants that are different from what your source code says. (It could also be a debugger bug ... but that is too large a coincidence.) However, the debugger output itself doesn't explain the behaviour of your app, because the app is not using those values.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks, Clear answer. Seems to be solved. Its debugger bug. Please check my updated question and new attached image – pleerock Oct 15 '12 at 04:54