Win a copy of Cloud Native PatternsE this week in the Cloud forum
or Natural Language Processing in the AI/ML forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Devaka Cooray
  • Liutauras Vilda
  • Jeanne Boyarsky
  • Bear Bibeault
Sheriffs:
  • Paul Clapham
  • Knute Snortum
  • Rob Spoor
Saloon Keepers:
  • Tim Moores
  • Ron McLeod
  • Piet Souris
  • Stephan van Hulst
  • Carey Brown
Bartenders:
  • Tim Holloway
  • Frits Walraven
  • Ganesh Patekar

Aren't Collection and AbstractMap peers? what's the common subclass?

 
Ranch Hand
Posts: 279
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well I thought Collection and AbstractMap are peers and that something like:
(AbstractMapRef instanceof Collection) should throw a compile time error, but this is not true, it does not give an error during compilation.
Now AbstractMap extends object and implements Map, all its direct subclasses has no relation to Collection interface....
so what's the common subclass that would make instanceof not cause a compile error? or does it have anything to do with the fact that AbstractMap is declared abstract?
I saw this piece of code in a question in Dan's exams, and I wonder, "should we know all the direct and indirect subclasses of Collection and Map interface", cause for the first look, I though AbstractMap probably implements Map and has no relation to Collection, they are peers, compile error... but I was wrong.
here's the question:

Answer is true,flase,true
The List and Set interfaces extend the Collection interface but Map does not. WeakHashMap implements Map and therefore is not an instance of Collection.
Any comments?
[ October 14, 2002: Message edited by: Alfred Kemety ]
[ February 04, 2003: Message edited by: Alfred Kemety ]
 
Author & Gold Digger
Posts: 7610
IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Map and Collection are the main interface of the Collections Framework but are completely distinct and unrelated. Realizations of the Collection interface are used to store objects as lists, sets, etc. Realizations of the map interface are used to store key-value pairs. What Map offers, though, is Collection views of its keys (keySet())and values (values()).
Please have a look at Thomas Paul's articles about the Collections Framework in the Javaranch newsletters.
Also have a look at the Collections trail in Sun's Java Tutorial.
[ October 14, 2002: Message edited by: Valentin Crettaz ]
 
mister krabs
Posts: 13974
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
They are distinct and unrelated which I think is Alfred's point. The statement:
if (myMap instance of Collection)
will give the compile error:
interfaces java.util.Map and java.util.Collection are incompatible; both define remove(java.lang.Object), but with different return type
but the statement:
if (myAbstractMap instanceof Collection)
compiles cleanly. But an AbstractMap can never be an instanceof a Collection so I should get a compile error.
 
Ranch Hand
Posts: 1865
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The compiler understands that a person is free to declare a class that extends AbstractMap and implements Collections. Although is is unlikely that anyone would do that the compiler understands that it could be done. At this time, no Java compiler is able to throw an IDoubtItException in response to a situation that is possible but unlikely.
The following code is an example of the fact that the compiler allows some flexibility with the use of the instanceof operator.

The above code compiles and prints "false,true".
 
Thomas Paul
mister krabs
Posts: 13974
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Actually you can't create a class that extends AbstractMap and implements Collection!
This simple code:
import java.util.*;
public abstract class MyAbstractMap extends AbstractMap implements Collection {}
will give this compile error:
C:\junk\MyAbstractMap.java:2: remove(java.lang.Object) in java.util.AbstractMap cannot implement remove(java.lang.Object) in java.util.Collection; attempting to use incompatible return type
found : java.lang.Object
required: boolean
public abstract class MyAbstractMap extends AbstractMap implements Collection {}
^
1 error
This is the reason that the compiler gives an error when you try to use instanceof with a Map object against a Collection. Why it understands this for Map and not for AbstractMap seems like a bug to me.
[ October 14, 2002: Message edited by: Thomas Paul ]
 
Dan Chisholm
Ranch Hand
Posts: 1865
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thomas,
You are correct. The compiler and I did not check for multiple inheritance issues between AbstractMap and Collection. Clearly, there are some limitations to what the compiler tests for in terms of the compatibility of the operands of the instanceof operator.
I wonder if these limitations are a feature or a bug? When was the last time James Gosling posted here?
To avoid this controversy in the future I think I'll change the exam question so that Object reference types are used rather than AbstractList, AbstactMap and AbstractSet.
 
Ranch Hand
Posts: 223
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi,
Dan, your exemple and the original code compile because we're dealing with interfaces. In both exemples A and Collection are interfaces. We can always cast between an interface and a non-final object. If you try to define A as a class or leave it the way it's and define c as a final class you would get a compile error.
 
Shishio San
Ranch Hand
Posts: 223
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi,
This should explain every thing
http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#20232


5.5 Casting Conversion
......
The remaining cases involve conversion between reference types. The detailed rules for compile-time correctness checking of a casting conversion of a value of compile-time reference type S (source) to a compile-time reference type T (target) are as follows:

If S is a class type:
If T is a class type, then S and T must be related classes-that is, S and T must be the same class, or S a subclass of T, or T a subclass of S; otherwise a compile-time error occurs.
If T is an interface type:
If S is not a final class (�8.1.1), then the cast is always correct at compile time (because even if S does not implement T, a subclass of S might).
If S is a final class (�8.1.1), then S must implement T, or a compile-time error occurs.
If T is an array type, then S must be the class Object, or a compile-time error occurs.
If S is an interface type:
If T is an array type, then T must implement S, or a compile-time error occurs.
If T is a class type that is not final (�8.1.1), then the cast is always correct at compile time (because even if T does not implement S, a subclass of T might).
If T is an interface type and if T and S contain methods with the same signature (�8.4.2) but different return types, then a compile-time error occurs.
If S is an array type SC[], that is, an array of components of type SC:
If T is a class type, then if T is not Object, then a compile-time error occurs (because Object is the only class type to which arrays can be assigned).
If T is an interface type, then a compile-time error occurs unless T is the type java.io.Serializable or the type Cloneable, the only interfaces implemented by arrays.
If T is an array type TC[], that is, an array of components of type TC, then a compile-time error occurs unless one of the following is true:
TC and SC are the same primitive type.
TC and SC are reference types and type SC can be cast to TC by a recursive application of these compile-time rules for casting.

 
Dan Chisholm
Ranch Hand
Posts: 1865
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Shishio San:

If S is not a final class (�8.1.1), then the cast is always correct at compile time (because even if S does not implement T, a subclass of S might).


Shishio,
Great post!
Your quote is a more precise statement of the point that I was trying to make earlier.


The compiler understands that a person is free to declare a class that extends AbstractMap and implements Collections. Although is is unlikely that anyone would do that the compiler understands that it could be done. At this time, no Java compiler is able to throw an IDoubtItException in response to a situation that is possible but unlikely.


Thomas also made a good point.


Actually you can't create a class that extends AbstractMap and implements Collection!


He then pointed out that both AbstractMap and Collection declare the same method but with different return types.
I think we can conclude that the compiler does not check for multiple inheritance problems involving a conflict between an interface and a class.
It appears that the JLS does indeed provide a full description of the behavior.
Do we all agree?
[ October 14, 2002: Message edited by: Dan Chisholm ]
 
Thomas Paul
mister krabs
Posts: 13974
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The problem I see it is this:
The compiler knows that no class can implement Map and Collection. It will give you an error if you try to compare the two with instanceof.
Therefore no class can extend AbstractMap (which implements Map) and Collection. This is demonstrated by my code sample.
Therefore since the condition is impossible, the compiler should be able to catch it. It can catch it for comparisons between Map and Collection buit fails for comparisons between AbstractMap and Collection.
This seems like a bug/mistake/or (in Microsoft terms) an undocumented feature.
 
Shishio San
Ranch Hand
Posts: 223
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The answer to Thomas question is:


If S is an interface type:
If T is an interface type and if T and S contain methods with the same signature (�8.4.2) but different return types, then a compile-time error occurs.


As for your last question Dan, The reason the compiler doesn't complain is that it assumes that a subclass MIGHT implement the interface in question. At runtime this assumption would be rejected if it turns out invalid.
You can read about the rules applied at runtime in the following:


If a cast to a reference type is not a compile-time error, there are two cases:

The cast can be determined to be correct at compile time. A cast from the compile-time type S to compile-time type T is correct at compile time if and only if S can be converted to T by assignment conversion (�5.2).
The cast requires a run-time validity check. If the value at run time is null, then the cast is allowed. Otherwise, let R be the class of the object referred to by the run-time reference value, and let T be the type named in the cast operator. A cast conversion must check, at run time, that the class R is assignment compatible with the type T, using the algorithm specified in �5.2 but using the class R instead of the compile-time type S as specified there. (Note that R cannot be an interface when these rules are first applied for any given cast, but R may be an interface if the rules are applied recursively because the run-time reference value may refer to an array whose element type is an interface type.) The modified algorithm is shown here:
If R is an ordinary class (not an array class):
If T is a class type, then R must be either the same class (�4.3.4) as T or a subclass of T, or a run-time exception is thrown.
If T is an interface type, then R must implement (�8.1.4) interface T, or a run-time exception is thrown.
If T is an array type, then a run-time exception is thrown.
If R is an interface:
If T is a class type, then T must be Object (�4.3.2), or a run-time exception is thrown.
If T is an interface type, then R must be either the same interface as T or a subinterface of T, or a run-time exception is thrown.
If T is an array type, then a run-time exception is thrown.
If R is a class representing an array type RC[]-that is, an array of components of type RC:
If T is a class type, then T must be Object (�4.3.2), or a run-time exception is thrown.
If T is an interface type, then a run-time exception is thrown unless T is the type java.io.Serializable or the type Cloneable, the only interfaces implemented by arrays (this case could slip past the compile-time checking if, for example, a reference to an array were stored in a variable of type Object).
If T is an array type TC[], that is, an array of components of type TC, then a run-time exception is thrown unless one of the following is true:
TC and RC are the same primitive type.
TC and RC are reference types and type RC can be cast to TC by a recursive application of these run-time rules for casting.

 
Dan Chisholm
Ranch Hand
Posts: 1865
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Thomas Paul:
It can catch it for comparisons between Map and Collection buit fails for comparisons between AbstractMap and Collection.
This seems like a bug/mistake/or (in Microsoft terms) an undocumented feature.



Section 15.20.2 of the Java Language Specification states the following.


RelationalExpression instanceof ReferenceType
...
If a cast of the RelationalExpression to the ReferenceType would be rejected as a compile-time error, then the instanceof relational expression likewise produces a compile-time error. In such a situation, the result of the instanceof expression could never be true.


However, the actual behavior is as follows.

The code compiles and prints "false" before throwing and exception at line 3.

In this second example compilation errors occur at lines 2 and 3.
Your point is that the compiler appears to ignore inherited methods when performing the compile time checks. Yes, that appears to be "undocumented behavior".
Am I still missing something here?
 
Alfred Kemety
Ranch Hand
Posts: 279
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Woooooh, Guess I started something here But the comments and replies were great, really good.. I'll try to make a summary to all of this and post it.. thanks for all your efforts.
[ October 14, 2002: Message edited by: Alfred Kemety ]
 
Thomas Paul
mister krabs
Posts: 13974
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Originally posted by Dan Chisholm:
Your point is that the compiler appears to ignore inherited methods when performing the compile time checks.

The compile ignores inherited methods when performing compile time checks on a class cast to an interface but does pay attention to them when interfaces are cast to interfaces. This seems inconsistent to me. There seems to be no technical reason for the compiler to be unable to reject the comparison. It is, in my humble opinion, a mistake in the language specification.
 
Ranch Hand
Posts: 2120
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think the compiler strictly adheres to the following points in JLS 5.5


# If S is a class type:
* If T is an interface type:
o If S is not a final class (�8.1.1), then the cast is always correct at compile time (because even if S does not implement T, a subclass of S might).


Even though an instance inheriting from AbstractMap could never implement Collection the cast is allowed at compile time.


# If S is an interface type:
* If T is an interface type and if T and S contain methods with the same signature (�8.4.2) but different return types, then a compile-time error occurs.

 
Consider Paul's rocket mass heater.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!