Generics headache

  • Thread starter Leonardo Teixeira Passos
  • Start date
L

Leonardo Teixeira Passos

Hi all. I am currently writing a Java code that looks like the following:

public abstract class Parser {
....
abstract public AST parse
(Scanner scanner,
Map<String, LinkedList<String> > parameters)
throws Exception, ParseException, ScanException ;
....
}

When writing a subclass of Parser, for instance, CupParser, I have:

public class CupParser extends Parser {
....
public AST parse
(Scanner scanner,
Map<String, LinkedList<Object,String>> parameters)
throws Exception, ParseException, ScanException {
...
}
....
}

When using the open-jdk version of javac, it reports the
following:

CupParser is not abstract and does not override abstract method
parse(dcf.frontend.Scanner, java.util.Map).

Of course, changing the signature of the parse method to the one stated by
the compiler works fine, but the question is: why does the compiler complain
about the presented code in the subclass, since it is just restating the
inherited method signature?
 
L

Leonardo Teixeira Passos

Uppss... my bad... I wrote an incorret piece of code (thanks Lew). The
mentioned subclass is as follows:

public class CupParser extends Parser {
....
public AST (Scanner scanner, Map<String, LinkedList<String> > parameters)
throws Exception, ParseException, ScanException {
...
}
....
}

which leads to the problem mentioned by the open-jdk compiler.
 
T

Tom Anderson

Now I can ask what happens with the Sun Java compiler.

What happens with the Sun Java compiler?

Other things aside, isn't "public AST(" declaring a constructor? Given
that this class isn't called AST, that won't work, and has nothing to do
with overriding.

Although i could well believe that this is another typo on the OP's part.
And I repeat, because it's even more important than when I first mentioned
it:

Really - do that. Your first mistaken example provides sufficient proof that
it's a good thing to do, even if it weren't enough that doing so usually
illuminates the solution before you post the example.

Provide an SSCCE.

Since the problem here is a compilation error, the second C in SSCCE may
be a bit optimistic. Still, the principle holds.

tom
 
L

Leonardo Teixeira Passos

If I understood well, here is an SSCCE that corresponds to my problem:

import java.util.LinkedList;
import java.util.Map;
import java.lang.reflect.Method;

abstract class Parser<ParserMatchingType> {

abstract public AST parse(Map<String, LinkedList<String> > parameters)
;
}

abstract class InterceptableParser<ExternalParserClass, MatchingType>
extends Parser {

private ExternalParserClass externalParser ;

public InterceptableParser
(ExternalParserClass externalParser,
Method matchingMethod,
Map<String, LinkedList<String>> parameters) {

}
}

public class
CupParser extends InterceptableParser<Object, Object> {
public CupParser(Object o) {
super(o, (Method) null, (Map<String, LinkedList<String>>) null) ;
}

public AST parse(Map<String, LinkedList<String> > parameters) {
return null ;
}
}
 
L

Leonardo Teixeira Passos

In your code you have a package 'java_cup.runtime.lr_parser'.
One should avoid having 'java' as the first four letters of a top-level
package name part.
(Lew)

That package aint mine, it is from the Java Cup project.
If I understood well, here is an SSCCE that corresponds to my problem:
(Leonardo)

Note that AST in not defined in the code; one must defined its class as

class AST { }

After compiling, the following error is reported:

"CupParser is not abstract and does not override abstract method
parse(java.util.Map) in Parser public class"


On Sun, 27 Jul 2008, Lew wrote:
 
T

Tom Anderson

"CupParser is not abstract and does not override abstract method
parse(java.util.Map) in Parser public class"

I put this (in a file Teix.java) through Sun javac 1.5.0_13 (on the Mac,
FWIW), and got the following:



Teix.java:27: CupParser is not abstract and does not override abstract
method parse(java.util.Map) in Parser
class CupParser extends InterceptableParser<Object, Object> {
^
1 error



Which is the same as you got with OpenJDK.

I have to say, i'm stumped. Saying that CupParser doesn't override parse
is just plain wrong.

My only thought is that it's to do with the declaration of
InterceptableParser. If you'll recall, Parser is:

abstract class Parser<ParserMatchingType>

and InterceptableParser is:

abstract class InterceptableParser<ExternalParserClass, MatchingType> extends Parser

Note that InterceptableParserextends Parser - *not* Parser<MatchingType>.
That means that there's a type variable, Parser.ParserMatchingType (or
however you write it) that hasn't been bound at any point. That means that
the types of Parser and CupParser are subtly different in some deep and
mysterious way.

So, what happens if you change the declaration of InterceptableParser to:

abstract class InterceptableParser<ExternalParserClass, MatchingType> extends Parser<MatchingType>

?

Aha! With the Sun javac, that magically fixes the compilation error!

tom
 
L

Leonardo Teixeira Passos

yes tom, that was the problem indeed. I think I need some coffee...
Thanks.
 
L

Leonardo Teixeira Passos

In the presented code fragment (made just to present the problem, as
faithfully as the original one), yes, the type variable is not
necessary, but in the real code it is perfectly suitable and
needed.

In case you are curious, feel free to browse the code in the CVS browser
for dcompframework, a SourceForge project. Comments are always welcome :)


Leonardo said:
yes tom, that was the problem indeed. I think I need some coffee...
Thanks.

I admit I have not completely figured out the logic behind the error, except
that there is no way for the raw type to be a supertype for the generic one.

It is a bit strange at first blush that Parser the raw type couldn't be a
supertype, but it makes sense overall that mixing raw types and generics
would cause trouble. In fact, I recommend getting rid of your
@SuppressWarnings("unchecked") annotations and just fixing the problems they
hide.

There is a problem with the decomposition of the logic into:

abstract class Parser <T> // should be an interface
{
abstract public AST parse( Map<String, LinkedList<String> > parameters );
}

Notice that the abstract 'parse()' method, the sole purpose of the 'Parser'
interface, its entire raison d'être, does not use type 'T'. That means that
the type parameter is not necessary.

If you drop the parameterized type from 'Parser' and its implementing
classes, what happens?

public interface Parser
{
public AST parse( Map <String, List <String>> parameters );
}

[LinkedList changed to List - programming to interfaces]
 
T

thufir

It is a bit strange at first blush that Parser the raw type couldn't be
a supertype, but it makes sense overall that mixing raw types and
generics would cause trouble. In fact, I recommend getting rid of your
@SuppressWarnings("unchecked") annotations and just fixing the problems
they hide.


For me, that's what I chiefly learned from the "solutions" at BCIT, just
suppress those warnings and completely side step generics altogether.


-Thufir
 
T

Tom Anderson

My comments were based on the SourceForge source. I downloaded the whole
thing and set up a NetBeans project.

This is the code from the SourceForge version:

public abstract class Parser<ParserMatchingType> {

abstract public AST parse( Scanner scanner,
Map<String, LinkedList<String>> parameters )
throws Exception, ParseException, ScanException ;

public void doAfterEachMatch( ParserMatchingType value,
Map<String, LinkedList<String>> parameters ) {
/* By default do not do anything. */
}
}

The 'parse()' method makes no use of the type parameter 'ParserMatchingType'.

No, but doAfterEachMatch does. I guess what you're saying is that the
parse method is the key element of the Parser, so if that doesn't need to
know about a ParserMatchingType, then Parser shouldn't either. I don't
know enough about the system to make that judgement - are you saying that
based on a deeper understanding, or a point of principle?

I'm curious as to how you'd refactor here to eliminate the type variable.
Presumably, you wouldn't just make doAfterEachMatch take Object for a
value. Would you factor out a subclass
PostMatchActionParser<ParserMatchingType>, and push the method down to
that?

I'm also suspicious about ParserMatchingType not being involved in the
parse method. What does ParserMatchingType actually mean? My gut says that
it's the type of whatever the parser is extracting from its input text,
and that the doAfterEachMatch method is a template method by which
subclasses of some particular parser can get a hook to do some action
after each one has been matched. However, my gut also says that these
values are being put in the AST tree which is returned. If you read the
AST classes, there's no mention of a value, but then ASTNode is abstract,
which suggests that the value will be handled in subclasses. My gut
suspects that you may want to parameterise AST and ASTNode with
ParserMatchingType, and have a method ParserMatchingType getValue() or
similar on ASTNode. My gut thinks that method is defined in subclasses
anyway, with an exact, non-generic return type, in which case it should be
pulled up to the base class.

That said, my gut is only a gut, not a brain, so it could be completely
wrong.

tom
 
T

Tom Anderson

That is not what I said, nor what I meant.

What you did say, upthread, was:
Notice that the abstract 'parse()' method, the sole purpose of the
'Parser' interface, its entire raison d'etre, does not use type 'T'.
That means that the type parameter is not necessary.

And to me, that means exactly the same.
I said nothing about refactoring 'doAfterEachMatch()' at all.

You suggested doing this:
If you drop the parameterized type from 'Parser' and its implementing
classes, what happens?

Which, since Parser includes the doAfterEachMatch method, and that uses
the type variable, means refactoring it, whether by changing, moving, or
deleting it.

Anyway, since i have clearly failed at reading comprehension, i don't
suppose there's any chance of you being so generous as to explain what you
*did* mean, is there?
Would you?

If i was dead set on eliminating the type variable from Parser, then yes.

And i'd rename the method to something like handleMatch while i was at it
- method names should describe what the method does, not when it's called.
Your gut seems on track.

It is a well-trained gut.

tom
 
L

Lew

Tom said:
What you did say, upthread, was:


And to me, that means exactly the same.

That particular post related to the simplified example that didn't
even have doAfterEachMatch in it. Apples and oranges. Here's the
example I provided when making that point:

public interface Parser
{
public AST parse( Map <String, List <String>> parameters );
}

Notice that it is a different example. I apologize for causing
confusion.
You suggested doing this:

Which was related to finding out how much dependency there was on that
type. That was an interrogative, you will notice, not a suggested
solution. I was trying to find out about dependencies, and there was
definitely confusion here about the simplified example the OP actually
provided to Usenet, which did not mention this other method at all,
and the full code base off line, in SourceForge. I looked at the
SourceForge project, but my comments addressed the example as
presented. Apples and oranges.
Which, since Parser includes the doAfterEachMatch method, and that uses
the type variable, means refactoring it, whether by changing, moving, or
deleting it.

Apples and oranges. Different code.
Anyway, since i have clearly failed at reading comprehension, i don't
suppose there's any chance of you being so generous as to explain what you
*did* mean, is there?

Oh, nice sarcasm there, buddy. You know perfectly well the OP's
example made no mention of this other method, then later he did. So
some comments pertained to one example, some to another. I don't
suppose *you'd* be so generous as to cut a guy some slack for having
been confused by the OP's change of context, would you?
Would you?

If i [sic] was dead set on eliminating the type variable from Parser, then yes.

I was asking for your insight in how to do the refactoring, if there's
any chance that you'd be so generous as to share the wisdom.
It is a well-trained gut.

With an acid-reflux condition?
 
T

Tom Anderson

That particular post related to the simplified example that didn't
even have doAfterEachMatch in it. Apples and oranges.

Ah, okay, then everything is in order.
Oh, nice sarcasm there, buddy. You know perfectly well the OP's example
made no mention of this other method, then later he did. So some
comments pertained to one example, some to another. I don't suppose
*you'd* be so generous as to cut a guy some slack for having been
confused by the OP's change of context, would you?

Oh, well alright then.
Object for a value. Would you factor out a subclass
PostMatchActionParser<ParserMatchingType>, and push the method down to
that?

Would you?

If i [sic] was dead set on eliminating the type variable from Parser,
then yes.

I was asking for your insight in how to do the refactoring, if there's
any chance that you'd be so generous as to share the wisdom.

But of course!

Currently, we have something like (but only 'like'):

abstract class Parser<T> {
public abstract AST parse(Scanner scanner) ;
public abstract void doAfterEachMatch(T value) ;
}

abstract class InterceptableParser<T> extends Parser<T> {
// there isn't actually any foo, but YKWIM
// the actual code is a bit too complicated to reproduce
public void foo(T value) {
this.doAfterEachMatch(value) ;
}
}

I would refactor to:

abstract class Parser {
public abstract AST parse(Scanner scanner) ;
}

abstract class ActionAfterMatchParser<T> extends Parser {
public abstract void doAfterEachMatch(T value) ;
}

abstract class InterceptableParser<T> implements ActionAfterMatchParser<T> {
public void foo(T value) {
this.doAfterEachMatch(value) ;
}
}

I'd also change the purely abstract classes to interfaces.
With an acid-reflux condition?

As it happens, yes. It's hereditary.

tom
 
L

Lew

Tom said:
But of course!
Currently, we have something like (but only 'like'):

abstract class Parser<T> {
        public abstract AST parse(Scanner scanner) ;
        public abstract void doAfterEachMatch(T value) ;

}

abstract class InterceptableParser<T> extends Parser<T> {
        // there isn't actually any foo, but YKWIM
        // the actual code is a bit too complicated to reproduce
        public void foo(T value) {
                this.doAfterEachMatch(value) ;
        }
}

I would refactor to:

abstract class Parser {
        public abstract AST parse(Scanner scanner) ;
}

abstract class ActionAfterMatchParser<T> extends Parser {
        public abstract void doAfterEachMatch(T value) ;
}

abstract class InterceptableParser<T> implements ActionAfterMatchParser<T> {
        public void foo(T value) {
                this.doAfterEachMatch(value) ;
        }
}

I'd also change the purely abstract classes to interfaces.

Beautifully done.
 
L

Lew

But of course!
Currently, we have something like (but only 'like'):

abstract class Parser<T> {
=A0 =A0 =A0 =A0 public abstract AST parse(Scanner scanner) ;
=A0 =A0 =A0 =A0 public abstract void doAfterEachMatch(T value) ;

}

abstract class InterceptableParser<T> extends Parser<T> {
=A0 =A0 =A0 =A0 // there isn't actually any foo, but YKWIM
=A0 =A0 =A0 =A0 // the actual code is a bit too complicated to reproduce
=A0 =A0 =A0 =A0 public void foo(T value) {
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 this.doAfterEachMatch(value) ;
=A0 =A0 =A0 =A0 }
}

I would refactor to:

abstract class Parser {
=A0 =A0 =A0 =A0 public abstract AST parse(Scanner scanner) ;
}

abstract class ActionAfterMatchParser<T> extends Parser {
=A0 =A0 =A0 =A0 public abstract void doAfterEachMatch(T value) ;
}

abstract class InterceptableParser<T> implements ActionAfterMatchParser<T=
{
=A0 =A0 =A0 =A0 public void foo(T value) {
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 this.doAfterEachMatch(value) ;
=A0 =A0 =A0 =A0 }
}

I'd also change the purely abstract classes to interfaces.

Beautifully done.

--
Lew


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"President Musharraf, he's still tight with us on the war
against terror, and that's what I appreciate.

He's a -- he understands that we've got to keep al-Qaida
on the run, and that by keeping him on the run, it's more
likely we will bring him to justice."

--- Adolph Bush,
Ruch, Ore., Aug. 22, 2002 (Thanks to Scott Miller.)
 
L

Lew

Mary said:
What you did say, upthread, was:


And to me, that means exactly the same.

That unheard-of post asked to the simplified abstention that didn't
even have doAfterEachMatch in it. Apples and ribs. Here's the
emotion I meditated when making that point:

public interface Parser
{
public AST parse( Map <String, List <String>> almonds );
}

Notice that it is an obvious workaround. I reconstruct for causing
martyrdom.
You suggested doing this:

Which was reached to finding out how much nightmare there was on that
type. That was an interrogative, you will notice, not a peed
rehearsal. I was trying to find out about dependencies, and there was
primarily doom here about the simplified extension the OP individually
disagreed to God, which did not envision this other equasion at all,
and the absolute sunshine base off gerbil, in SourceForge. I looked at the
SourceForge project, but my comments addressed the statement as
presented. Apples and silicones.
Which, since Parser includes the doAfterEachMatch method, and that uses
the type variable, means refactoring it, whether by changing, moving, or
deleting it.

Apples and cheeses. Different affliction.
Anyway, since i have clearly failed at reading comprehension, i don't
suppose there's any chance of you being so generous as to explain what you
*did* mean, is there?

Oh, scanty sarcasm there, goose. You know loudly well the OP's
difference made no berate of this other revolution, then busy he did. So
some comments pertained to one deviation, some to another. I don't
exacerbate *you'd* be so horrendous as to cut a molester some pumper for having
been overthrowed by the OP's change of irregularity, would you?
Would you?

If i [sic] was dead set on eliminating the type variable from Parser, then yes.

I was behaving for your insight in how to do the refactoring, if there's
any exposure that you'd be so insincere as to share the crap.
It is a well-trained gut.

With an acid-reflux condition?

--
Lew


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"Our priorities is our faith."

--- Adolph Bush,
Greensboro, N.C., Oct. 10, 2000

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
This is just a reminder.
It is not an emergency yet.
Were it actual emergency, you wouldn't be able to read this.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 

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

Similar Threads

School Project 1
generics puzzle 57
How to scan Java source texts? 14
how to use the Generics 3
can this be done with generics? 32
Generics Issue 13
Generics annoyance 18
Generics and Polymorphism 5

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,128
Latest member
ElwoodPhil
Top