Incompatable conditional operand types

C

carl.manaster

Hi,

I'm in the midst of refactoring a "Peak" class, where I noticed a lot
of duplication depending on whether I was in the front half of the back
half of the peak. I got it nicely boiled down into two private nested
classes, Before and After, which extend Half, which implements IHalf.
Each of these, IHalf, Half, Before, and After, is private to the Peak
class. I'll probably be getting rid of the interface before too long,
but I mention it for the sake of telling the full story.

The Peak has
private final Before before
and
private final After after,
which are initialized in the constructor


Now there's a point in the Peak logic where I want to call some methods
of one half or the other, depending. When I do this:

if (heightBefore() > heightAfter())
return meanA(after);
else
return meanA(before);

it works fine, but I thought I would go ahead and inline the meanA
stuff and assign a local variable to be after or before, depending.
Like this:

Half half = heightBefore() > heightAfter() ? after : before;

And it doesn't like this line. It tells me, "incompatible conditional
operand types After and Before". The following two lines compile
without complaint:

Half half = heightBefore() > heightAfter() ? after : after;
Half half = heightBefore() > heightAfter() ? before: before;

(in separate compile events, of course), so I know that the instance
variables are, as I thought, of subtypes of Half.

The only previous message I found in the archives about this topic is a
year old and mentions Eclipse, so I guess it's worth pointing out that
that is my development environment, too. Is this an Eclipse problem, a
Java problem, or a Carl problem?

Thanks for any help.

Peace,
--Carl
 
C

Chris Smith

Half half = heightBefore() > heightAfter() ? after : before;

And it doesn't like this line. It tells me, "incompatible conditional
operand types After and Before".

That code is illegal in Java 1.4, but legal in Java 1.5. For 1.4, the
relevant section of the Java Language Specification is 15.25, which
says:

"If the second and third operands are of different reference types,
then it must be possible to convert one of the types to the other
type (call this latter type T) by assignment conversion (§5.2); the
type of the conditional expression is T. It is a compile-time error
if neither type is assignment compatible with the other type."

Since after can't be assigned to type of before, and before can't be
assigned to the type of after, the expression is illegal. You can solve
the problem by casting to the desired type explicitly in one or both of
the latter two operands.

Half half = heightBefore() > heightAfter() ? (Half) after : before;

The rules defined in Java 1.4 are conservative... that is, there are
safe operations such as yours that they exclude. But they are the rules
nevertheless.

Java 1.5 modifies that statement in section 15.25 to require a capture
conversion of the "lub" type operator, which is defined for type
parameter inference of methods as part of generics. The new version is
less conservative, and accepts your code as legal. In Java 1.5, the
resulting type of that expression is some beastly thing that's not
representable in Java at all, but is nevertheless assignment compatible
with Half. Indeed, it even accepts this rather surprising code:

public class Test
{
private static class A { }
private static interface X { }
private static class B extends A implements X { }
private static class C extends A implements X { }

public static void main(String[] args)
{
boolean b = true;
A a = b ? new B() : new C();
X x = b ? new B() : new C();
}
}

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

carl.manaster

Thanks, Chris,

for a very clear explanation. It's nice to know that my preferred
approach will work with 1.5.

Peace,
--Carl
 
C

Chris Smith

for a very clear explanation. It's nice to know that my preferred
approach will work with 1.5.

No problem. It was fun doing the research. I did find one bug in
Eclipse, though. The following code compiles in javac and ought to
compile according to the 1.5 spec, but fails in Eclipse 3.2M3.

public class Test
{
public static void main(String[] args)
{
boolean b = true;
Object obj = b ? true : 17.3;
}
}

This isn't too surprising. I've noticed that Eclipse's compiler often
has trouble applying boxing/unboxing conversions in places where they
should happen.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
J

Joseph Dionne

Chris said:
for a very clear explanation. It's nice to know that my preferred
approach will work with 1.5.


No problem. It was fun doing the research. I did find one bug in
Eclipse, though. The following code compiles in javac and ought to
compile according to the 1.5 spec, but fails in Eclipse 3.2M3.

public class Test
{
public static void main(String[] args)
{
boolean b = true;
Object obj = b ? true : 17.3;
}
}

This isn't too surprising. I've noticed that Eclipse's compiler often
has trouble applying boxing/unboxing conversions in places where they
should happen.

Do want something like this?

public class tConditional
{
static class Half { }
static interface IHalf { }
static class Before extends Half implements IHalf { }
static class After extends Half implements IHalf { }
static class Result extends Half { }

static public void main(String[] args)
{
Before before = new Before();
After after = new After();
Half half = null;

Result result = (1 == 1) ? before : after; // OK
Object half = (1 == 1) ? before : after; // OK
Half half = (1 == 1) ? before : after; // compile error
}
}

Using a third class that extends Half and does not implement IHalf to receive
conditional return, or casting half to Object both compile fine.

Joseph
 
C

Chris Smith

Joseph Dionne said:
Result result = (1 == 1) ? before : after; // OK
Object half = (1 == 1) ? before : after; // OK
Half half = (1 == 1) ? before : after; // compile error

That's not how it should be, or what I see in testing. The first line
shouldn't compile... either of the latter two should be fine.

In any case, you're using Java 1.5. Carl was working in Java 1.4 or
earlier, and the cast to Half from one of the result arguments solved
the problem.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
J

John C. Bollinger

Joseph said:
public class tConditional
{
static class Half { }
static interface IHalf { }
static class Before extends Half implements IHalf { }
static class After extends Half implements IHalf { }
static class Result extends Half { }

static public void main(String[] args)
{
Before before = new Before();
After after = new After();
Half half = null;

Result result = (1 == 1) ? before : after; // OK
Object half = (1 == 1) ? before : after; // OK
Half half = (1 == 1) ? before : after; // compile error
}
}

Using a third class that extends Half and does not implement IHalf to
receive conditional return, or casting half to Object both compile fine.

As Chris wrote, the first assignment should not compile: the right-hand
expression is legal, but its type is not assignment-compatible with that
of the left-hand side. This is a different issue than the one OP's
question concerned.

The other two assignments both should compile, once the little problem
with duplicate variable names is cleared up, but Sun's javac through
1.5.0_06 has a known bug that causes it to reject the third. (Eclipse
accepts both the second and the third, however.)
 
C

carl.manaster

Thanks, Joseph,

But Chris' reply led me to a solution I'm happy with; I simply declared
both members to be of type Half rather than the more specific subtypes.
Just one would have worked, but both seemed more symmetrical.

Half after = new After();
Half before = new Before();

Problem solved.

Peace,
--Carl
 
C

Chris Smith

Thanks, Joseph,

But Chris' reply led me to a solution I'm happy with

Glad to hear it. Now we've hijacked your thread to talk about other
expansions of the question that are interesting. No offense; it happens
all the time. We're not necessarily talking to you any more (unless, of
course, you are still interested).

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,794
Messages
2,569,641
Members
45,354
Latest member
OrenKrause

Latest Threads

Top