JDK-6448707 : Dot access to type parameters' type parameters
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: solaris_10
  • CPU: sparc
  • Submitted: 2006-07-13
  • Updated: 2006-07-25
  • Resolved: 2006-07-19
Description
A DESCRIPTION OF THE REQUEST :
For type parameters that take type parameters, dot access to the inner type parameters would remove the chore of matching up all the parts of more complex type parameters.

For example, public interface Digraph<Node,Edge> takes two type parameters. Type parameter lists that involve Digraph (like Subgraph) get complex quickly.

public interface Subgraph<Node,Edge,Supergraph extends Digraph<Node,Edge>>
    extends Digraph<Node,Edge>

Using Subgraph requires matching up Node and Edge type specifiers in two spots. Dot access to contained type parameters would let the compiler do the work for us.

public interface Subgraph<Supergraph extends Digraph>
    extends Digraph<Supergraph.Node,Supergraph.Edge>


See http://weblogs.java.net/blog/dwalend/archive/2006/05/tilting_at_the_1.html for more details.

JUSTIFICATION :
People whine about generics way too often. This change won't stop the simpering, but will save one real headache I've encountered with generics. My JDigraph project is exploring general purpose algorithms written in Java. Having dot access to type parameters would be much easier to work with than the current system. Lining up a nested parameter through several related objects is not so bad, but lining up five is really hard.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I'd like to be able to have access to a type parameter's type parameters within my java code, instead of having to have long lists of repetitive type parameters. For example, things like

public interface Subgraph<Supergraph extends Digraph>
    extends Digraph<Supergraph.Node,Supergraph.Edge>
{
   public Supergraph.Edge getEdge(Supergraph.Node fromNode,Supergraph.Node toNode);
}
ACTUAL -
instead of giving me the syntax error i deserve, bad things happen inside javac:

An exception has occurred in the compiler (1.5.0_06). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report.  Thank you.
java.lang.NullPointerException
	at com.sun.tools.javac.comp.Attr.selectSym(Attr.java:1799)
	at com.sun.tools.javac.comp.Attr.selectSym(Attr.java:1837)
	at com.sun.tools.javac.comp.Attr.visitSelect(Attr.java:1724)
	at com.sun.tools.javac.tree.Tree$Select.accept(Tree.java:987)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:275)
	at com.sun.tools.javac.comp.Attr.attribType(Attr.java:303)
	at com.sun.tools.javac.comp.Attr.attribTypes(Attr.java:346)
	at com.sun.tools.javac.comp.Attr.visitTypeApply(Attr.java:2237)
	at com.sun.tools.javac.tree.Tree$TypeApply.accept(Tree.java:1058)
	at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:275)
	at com.sun.tools.javac.comp.Attr.attribType(Attr.java:303)
	at com.sun.tools.javac.comp.Attr.attribBase(Attr.java:376)
	at com.sun.tools.javac.comp.MemberEnter.complete(MemberEnter.java:817)
	at com.sun.tools.javac.code.Symbol.complete(Symbol.java:355)
	at com.sun.tools.javac.code.Symbol$ClassSymbol.complete(Symbol.java:612)
	at com.sun.tools.javac.code.Symbol$ClassSymbol.flags(Symbol.java:550)
	at com.sun.tools.javac.comp.Resolve.isAccessible(Resolve.java:118)


---------- BEGIN SOURCE ----------
package net.walend.digraph;

import java.util.Iterator;
import java.util.Set;

import net.walend.collection.HasState;
import net.walend.collection.Bag;

import net.walend.collection.HasState;

/**
Digraph is an interface for representing directed graphs of nodes linked by zero or one edge. Each node and edge must be a unique Object in the digraph. Reusing edges and nodes may produce unpredictable results.
<p>
By default, this digraph uses equals() as the method to determine identity.
<p>
One could create a subclass of Digraph that will work with zero or more edges by extending this interface. I have not done that.
<p>
Direct implementations of Digraph should have a single constructor that takes a Digraph as a parameter.

@author @dwalend@
@since 20010612
*/
public interface Digraph<Node,Edge>
{
    public int nodeCount();

    public int edgeCount();

    public boolean isEmpty();

    public boolean containsNode(Object node);

/**
Returns true if the digraph contains any edge from fromNode to toNode

@throws NodeMissingException if either node is missing from the digraph.
*/
    public boolean containsEdge(Object fromNode,Object toNode) throws NodeMissingException;

    /**
@throws NodeMissingException if node is not in the digraph.
     */
    public int countInboundEdges(Node node) throws NodeMissingException;


    /**
@throws NodeMissingException if node is not in the digraph.
     */
    public int countOutboundEdges(Node node) throws NodeMissingException;

/**
Returns the set of nodes that can reach this node by crossing one edge.

@throws NodeMissingException if node is not in the digraph.
*/
    public Set<Node> getFromNodes(Node node) throws NodeMissingException;

/**
Returns the set of nodes that can be reached from this node by crossing one edge.

@throws NodeMissingException if node is not in the digraph.
*/
    public Set<Node> getToNodes(Node node) throws NodeMissingException;

/**
Implementations should explicitly state how they interpret nodeIterator()'s remove method. It should either throw an UnsupportedOperationException or cause a hidden side effects of removing edges.
*/
    public Iterator<Node> nodeIterator();

    public Set<Node> getNodes();

/**
Returns true if this Digraph has no edges.
*/
    public boolean isEdgeFree();

    public boolean containsNodes(Set<?> nodes);
    
/**
Returns true if edge links fromNode to toNode

@throws NodeMissingException if either node is missing from the digraph.
*/
    public boolean containsEdge(Object fromNode,Object toNode,Object edge) throws NodeMissingException;

 
    public Bag<Edge> getEdges();

/**
Returns the empty set if node has no inbound edges.

@throws NodeMissingException if node is not in the digraph.
*/
    public Bag<Edge> getInboundEdges(Node node) throws NodeMissingException;

/**
Returns the empty set if node has no outbound edges.

@throws NodeMissingException if node is not in the digraph.
*/
    public Bag<Edge> getOutboundEdges(Node node) throws NodeMissingException;

/**
Returns null if no edge links fromNode to toNode

@throws NodeMissingException if either node is missing from the digraph.
*/
    public Edge getEdge(Node fromNode, Node toNode) throws NodeMissingException;

/**
Returns true if digraph is a subgraph of this Digraph.
*/
    public boolean containsDigraph(Digraph<?,?> digraph);

/**
Returns true if digraph is the same as this; that is, if this.containsDigraph(digraph) and digraph.containsDigraph(this).
 */
    public boolean sameDigraphAs(Digraph<?,?> digraph);

}

/*@license@*/
----

package net.walend.subgraph;

import net.walend.digraph.Digraph;

/**
Subgraph is an interface for representing a Digraph that is a subset of another Digraph.

@author @dwalend@
@since alpha-0-8
*/
//public interface Subgraph<Node,Edge,Supergraph extends Digraph<Node,Edge>>
//    extends Digraph<Node,Edge>
public interface Subgraph<Supergraph extends Digraph>
    extends Digraph<Supergraph.Node,Supergraph.Edge>
{
       /**
Returns true if this subgraph is still a subgraph of its supergraph
     */
    public boolean valid();

    /**
Returns the digraph that this Subgraph is built on.
    */
    public Supergraph getSupergraph();
}

/*@license@*/package net.walend.subgraph;

import net.walend.digraph.Digraph;

/**
Subgraph is an interface for representing a Digraph that is a subset of another Digraph.

@author @dwalend@
@since alpha-0-8
*/
//public interface Subgraph<Node,Edge,Supergraph extends Digraph<Node,Edge>>
//    extends Digraph<Node,Edge>
public interface Subgraph<Supergraph extends Digraph>
    extends Digraph<Supergraph.Node,Supergraph.Edge>
{
       /**
Returns true if this subgraph is still a subgraph of its supergraph
     */
    public boolean valid();

    /**
Returns the digraph that this Subgraph is built on.
    */
    public Supergraph getSupergraph();
}

/*@license@*/

----

package net.walend.digraph;

import java.io.Serializable;

/**

@author @dwalend@
@since 20010620
*/

public class NodeMissingException
{
    private static final long serialVersionUID = 0L;
    
    private Object missingNode;

    public static final String DEFAULT = " is not in the Digraph";

    public NodeMissingException(Object node)
    {
        super(node.toString()+DEFAULT);
        missingNode = node;
    }

    public NodeMissingException(String message,Object node)
    {
        super(message+": "+node.toString());
        missingNode = node;
    }

    public NodeMissingException(Throwable nester,Object node)
    {
        super(node.toString()+DEFAULT,nester);
        missingNode = node;
    }

    public NodeMissingException(String message, Throwable nester,Object node)
    {
        super(message+": "+node.toString(), nester);
        missingNode = node;
    }

    public Object getNode()
    {
        return missingNode;
    }

}

/*
@license@
*/
---------- END SOURCE ----------

Comments
EVALUATION Responding to SDN comments: We are well aware of the tediousness of using generics in some cases and are investigating means to make it easier to *use* generic classes. However, this particular RFE is focused on making it easier to define a generic class. This is not as high a priority and the concrete suggestion is to overload or redefine syntax that already has a particular meaning: interface PowerCollection<Col extends Collection> extends Collection<Col> Consider the part "PowerCollection<Col extends Collection>". It already has a well defined meaning in the language since you can use generic classes with type arguments (that is a raw type). So this means that Col is a type variable that must hold a subtype of the raw type Collection which doesn't have any type variables. A note on style: we don't recommend having that many type parameters on your classes. Most often that not, all these parameters are redundant and the FloydWarshall class illustrates this nicely. This should be sufficent: class FloydWarshall<B extends IndexedDigraph<?, ?>, L extends IndexedMutableOverlayDigraph<?, ?, ?, B>, S extends Semiring<?, ?, ?, B, L>> The rest of the type arguments are not used in the class body. If this also true for the super types of FloydWarshall, then this class is over parameterized. Furthermore, I notice that the class doesn't really use the type arguments for anything substantial and this would work just as fine: class FloydWarshall extends ... { public <B extends IndexedDigraph<?, ?>, L extends IndexedMutableOverlayDigraph<?, ?, ?, B>, S extends Semiring<?, ?, ?, B, L>> L computeLabels(S semiring,B baseDigraph) { ... } ... } What I'm trying to say is that when you find yourself needing five type variables, your gut reaction should be to cut down on the number of parameters, not design language feautre ;-)
25-07-2006

EVALUATION Upon further reflection, I have decided to close this as will not fix. The proposed solution is to assign special meaning to this declaration: public interface Subgraph<Supergraph extends Digraph> ... However, this already has a meaning in the language (perhaps more obvious if you consider this example): class NumberList<N extends Number> implements List<N> ...
19-07-2006

EVALUATION The reported crash is not reproducible in newer mustang builds (JDK 6). Regarding the suggested language change, don't get your hopes up. Explicit declarations are favored in the Java programming language as it generally makes code easier to read (at the expense of writing it). The submitter suggest that this example is too verbose or tedious to write: public interface Subgraph<Node,Edge,Supergraph extends Digraph<Node,Edge>> extends Digraph<Node,Edge> The example does not follow the suggested coding conventions and perhaps following them would make the example easier on the eyes: /** * ... * @param <N> the type of nodes * @param <E> the type of edges * @param <S> the type of ... */ public interface Subgraph<N, E, S extends Digraph<N,E>> extends Digraph<N, E> { ... } However, I have dispatched this RFE to java/specification for further consideration.
17-07-2006