简体   繁体   中英

Compile error because of type check or ternary operator?

Recently I have got a compilation error in the code below:

import org.eclipse.swt.widgets.TreeItem;
Object parent; // can only be a Tree or a TreeItem    
...
TreeItem item = new TreeItem((parent instanceof TreeItem) ? (TreeItem) parent : (Tree) parent, SWT.NONE);

Compiler says: "The constructor TreeItem(Widget, int) is undefined"

Then I have tried it with another code:

Object x = new Integer(1);

Test t = new Test((x instanceof String) ? (String) x : (Integer) x);

class Test{
    public Test(String s){}
    public Test(Integer i){}
}

And got another error: "The constructor Test(Object&Serializable&Comparable) is undefined"

So I was forced to use the traditional if-else structure. Any ideas why the compiler behaves so?

JLS §15.25 describes the ternary operator.

Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2.

The type of the conditional expression is the result of applying capture conversion ( §5.1.10 ) to lub(T1, T2) ( §15.12.2.7 ).

Basically, you can think of the ternary operator as a method: it can only have one "return type". Since String and Integer are two different objects, it finds the common superclass and all interfaces implemented by both and creates a return type from that. ( String and Integer both implement Serializable and Comparable and extend Object , so you get Object & Serializable & Comparable .)

The reason is very simple: the Ternary-Operators result is a static type. When making such a typecasting, the type that will returned is the common ancestor of the type used in both possible results. In your first example widget is the common ancestor of TreeItem and Tree, in the second example the common ancestor from String and Integer is Object. So when you use the result of such an operation for a new operation, you need a constrcutor for that common type.

Seems like we are talking about this TreeItem class. Note that the constructor accepts either Tree or TreeItem .

Now, you are trying to cast that Object to correct type using instanceof . So far so good (however the design is a bit controversial). Unfortunately the ternary operator expression type must be resolved at compile time. The most specific common super class of both TreeItem and Tree is Widget , compare with this method:

public Widget smartCast(Object parent) {
    if(parent instanceof TreeItem) {
        return (TreeItem)parent;
    } else {
        return (Tree)parent;
    }
}

smartCast cannot have any other return type than Widget , just like your ternary operator.

when creating an object it looks like java needs to know which specific constructor you want to use at Compile time.

The reason its giving that odd error, The constructor Test(Object&Serializable&Comparable) is because Integer and String both inherit from those 3 (Object, Serializable, Comparable). And so at compile time, if you had a constructor which took any of those, Java would know you want to use that one.

Its the same with Tree and TreeItem. Their common parent it Widget. So it needs 1 constructor that can take both items.

It cant at Runtime pick between 2 different constructors for one new statement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM