Re: Scala to Java Generics Problem?
Bill Venners <bill <at> artima.com>
2009-09-01 04:14:04 GMT
Hi Eric,
I think this problem is different from the problem Emil Ivanov posted
about, but it looks a lot like the potential compiler bug that allac
was seeing. Because you've declared the type of enum to be
Enumeration[_], which should be the return type of getHeaderNames as
seen from Scala. But getHeaderNames' return type is a Java raw type,
and for some reason you're getting this weird compiler error message.
I'll wrap up the little code example I made up and submit a bug
report.
Bill
On Mon, Aug 31, 2009 at 8:41 PM, Eric Torreborre<etorreborre <at> yahoo.com> wrote:
>
>> Thus you have the problem of turning that into an Enumeration[String],
> which you'll need to do with a cast as you suggested. If that sounds
> unsafe to you, that's because it is, but it is because you need to
> call into an ungenerified Java library.
>
> Actually, if we do this (using specs and Mockito):
>
> object test extends org.specs.Specification with org.specs.mock.Mockito {
> val raw = mock[HttpServletRequest]
> val enum: java.util.Enumeration[_] = new java.util.Enumeration[String] {
> val headers = Array("en-us", "bg;q=0.9")
> private var current = 0
> def nextElement: String = {
> headers(current)
> }
> def hasMoreElements: Boolean = {
> current < headers.length - 1
> }}
>
> raw.getHeaderNames() returns enum // error here
>
> }
>
> We get the same type of compilation error:
>
> type mismatch;
> found : java.util.Enumeration[_$1] where type _$1
> required: java.util.Enumeration[?0] where type ?0
>
> I've been seeing this several times when dealing with pre-java 1.5 code and
> found very annoying to be unable to declare in Scala a type which would be
> equivalent to an ungenericized Java type. But I may be wrong and missing
> something there also.
>
> E.
>
>
> Bill Venners-3 wrote:
>>
>> Hi Eric,
>>
>> On Mon, Aug 31, 2009 at 7:19 PM, Eric Torreborre<etorreborre <at> yahoo.com>
>> wrote:
>>>
>>> Hi Bill,
>>>
>>> I'm really interested in understanding why this works by using your
>>> EasyMock
>>> trait. It is still calling EasyMock.expect right? If anyone else also has
>>> a
>>> clue on this,...
>>>
>> My theory is it is a compiler bug. So the bug shows up for some reason
>> in one case, but not the other.
>>
>> That said, I'm old enough to remember five years that I spent
>> programming in C++. During those years there were about three
>> occasions in which I was absolutely completely convinced the C++
>> compiler had a bug. But it always turned out to be my bug. This was a
>> lesson in humility that I carried with me into my Java and now Scala
>> years. But given I came to Scala much earlier stage in the languages
>> evolution than I did to the earlier languages, I've actually stumbled
>> upon Scala compiler bugs turned out to *not* be my bug. Hard to say if
>> this is a real one or not, but I sure can't see anything wrong with
>> the code with my compiler-eye.
>>
>>> Moreover, this issue already popped up in a different form, though when
>>> trying to return java.util.Enumeration values (un-genericized ones, see
>>> my
>>> previous post for a link). So I suspect that there is a potential issue
>>> (bug?) with the Java-Scala integration.
>>>
>> I looked at that and I think that's not a compiler bug:
>>
>> http://groups.google.com/group/specs-users/browse_thread/thread/e97fd63c434dd59b
>>
>> I think what you have there is an ungenerified Java library, which
>> appears in Scala as if it is parameterized with an existential type.
>>
>> [error] found : java.lang.Object with java.util.Enumeration[String]{
>> ... }
>> [error] required: java.util.Enumeration[?0] where type ?0
>> [error] new java.util.Enumeration[String] {
>> [error] ^
>>
>> What's coming back from getHeaders as far as the Scala compiler is
>> concerned is:
>>
>> java.util.Enumeration[_]
>>
>> Thus you have the problem of turning that into an Enumeration[String],
>> which you'll need to do with a cast as you suggested. If that sounds
>> unsafe to you, that's because it is, but it is because you need to
>> call into an ungenerified Java library. So you shouldn't feel guilty
>> about the cast. Even though it is unsafe, it's your only way back to
>> type safety. I think about the best thing Emil can do if he's doing
>> this cast repeatedly is factor the cast out into one place and call
>> that method multiple times instead of hard coding the cast in multiple
>> places.
>>
>> Bill
>>
>>> Eric.
>>>
>>>
>>> Bill Venners-3 wrote:
>>>>
>>>> Hi allac,
>>>>
>>>> I investigated this and was able to reproduce the type error:
>>>>
>>>> import org.scalatest.FunSuite
>>>> import org.scalatest.mock.EasyMockSugar
>>>> import org.easymock.EasyMock.{expect => expectCall, replay, verify,
>>>> createMock}
>>>>
>>>> class MySuite extends FunSuite {
>>>>
>>>> test("easymock issue") {
>>>>
>>>> trait TopologyModel extends GetElementaryStreamsMethod
>>>>
>>>> val mockTopologyModel = createMock(classOf[TopologyModel])
>>>>
>>>> var streams: java.lang.Iterable[_ <: ElementaryStream] = new
>>>> java.util.ArrayList()
>>>>
>>>>
>>>> expectCall(mockTopologyModel.getAllElementaryStreams).andStubReturn(streams)
>>>>
>>>> replay(mockTopologyModel)
>>>> val result = mockTopologyModel.getAllElementaryStreams
>>>> assert(result === streams)
>>>> verify(mockTopologyModel)
>>>> }
>>>> }
>>>>
>>>> /*
>>>> The Java files are:
>>>>
>>>> // In ElementaryStream.java
>>>> public class ElementaryStream {}
>>>>
>>>> // In GetElementaryStreamsMethod.java
>>>> public interface GetElementaryStreamsMethod {
>>>> Iterable<? extends ElementaryStream> getAllElementaryStreams();
>>>> }
>>>> */
>>>>
>>>> If I used the full forSome form:
>>>>
>>>> var streams: java.lang.Iterable[T] forSome { type T <:
>>>> ElementaryStream } = new java.util.ArrayList()
>>>>
>>>> I'd get the same error message:
>>>>
>>>> found : java.lang.Iterable[_$1] where type _$1 <: ElementaryStream
>>>> required: java.lang.Iterable[?0] where type ?0 <: ElementaryStream
>>>>
>>>> expectCall(mockTopologyModel.getAllElementaryStreams).andStubReturn(streams)
>>>>
>>>> ^
>>>>
>>>> I can't find anything wrong with these types so it may be a compiler
>>>> bug. I can submit it if you haven't already.
>>>>
>>>> I did find a workaround, though I don't know why this works when the
>>>> previous one does not. But I had added an EasyMockSugar trait to
>>>> ScalaTest 1.0 recently. Using that trait you get no compiler error.
>>>> (Actually the only thing you'd need to use is the mock method of trait
>>>> EasyMockSugar. You can just use that and nothing else from ScalaTest,
>>>> but you'd have to grab the snapshot as this only in 1.0). This
>>>> compiles and runs fine:
>>>>
>>>> import org.scalatest.FunSuite
>>>> import org.scalatest.mock.EasyMockSugar
>>>>
>>>> class MyOtherSuite extends FunSuite with EasyMockSugar {
>>>>
>>>> test("easymock issue") {
>>>>
>>>> trait TopologyModel extends GetElementaryStreamsMethod
>>>>
>>>> val mockTopologyModel = mock[TopologyModel]
>>>>
>>>> var streams: java.lang.Iterable[_ <: ElementaryStream] = new
>>>> java.util.ArrayList()
>>>>
>>>> expecting {
>>>>
>>>> call(mockTopologyModel.getAllElementaryStreams).andStubReturn(streams)
>>>> }
>>>>
>>>> whenExecuting(mockTopologyModel) {
>>>> val result = mockTopologyModel.getAllElementaryStreams
>>>> assert(result === streams)
>>>> }
>>>> }
>>>> }
>>>>
>>>> Bill
>>>>
>>>> On Sun, Aug 30, 2009 at 5:08 AM, alIac<java.peritus <at> gmail.com> wrote:
>>>>>
>>>>> I'm trying to add an easymock expectation for a java method defined as
>>>>> follows:
>>>>>
>>>>> Iterable<? extends ElementaryStream> getAllElementaryStreams();
>>>>>
>>>>> The expectation is set up as follows:
>>>>>
>>>>>
>>>>> var streams:java.lang.Iterable[_ <: ElementaryStream] =
>>>>> Lists.newArrayList()
>>>>>
>>>>> expect(mockTopologyModel.getAllElementaryStreams).andStubReturn(streams)
>>>>>
>>>>>
>>>>> During compile I get the following:
>>>>>
>>>>> Error:Error:line (61)error: type mismatch;
>>>>> found : java.lang.Iterable[_$1] where type _$1 <:
>>>>> com.foo.vms.topology.core.ElementaryStream
>>>>> required: java.lang.Iterable[?0] where type ?0 <:
>>>>> com.foo.vms.topology.core.ElementaryStream
>>>>> expect(mockTopologyModel.getAllElementaryStreams).andStubReturn(streams)
>>>>>
>>>>> I can't seem to define the streams var correctly.
>>>>> --
>>>>> View this message in context:
>>>>> http://www.nabble.com/Scala-to-Java-Generics-Problem--tp25211005p25211005.html
>>>>> Sent from the Scala mailing list archive at Nabble.com.
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> Bill Venners
>>>> Artima, Inc.
>>>> http://www.artima.com
>>>>
>>>>
>>>
>>> --
>>> View this message in context:
>>> http://www.nabble.com/Scala-to-Java-Generics-Problem--tp25211005p25233413.html
>>> Sent from the Scala mailing list archive at Nabble.com.
>>>
>>>
>>
>>
>>
>> --
>> Bill Venners
>> Artima, Inc.
>> http://www.artima.com
>>
>>
>
> --
> View this message in context: http://www.nabble.com/Scala-to-Java-Generics-Problem--tp25211005p25233872.html
> Sent from the Scala mailing list archive at Nabble.com.
>
>
--
--
Bill Venners
Artima, Inc.
http://www.artima.com