Inheritance and generic type inference

Hi all,

1. Why the code below (see (1) and (2)) does not work?

2. Is there another way for (3) to make it working?

Thx for any help!

J

import java.util.ArrayList;

import java.util.Collection;

publicclass GenericsTest{

staticinterface Part{

void info();

}

staticinterface Vehicle<Textends Part>{

void addPart(T part);

Collection<T> getParts();

}

staticclass VehicleParc<Textends Vehicle><Part>>{

private Collection<T> vehicles =new ArrayList<T>();

void addVehicle(T vehicle){

vehicles.add(vehicle);

}

Collection<T> getVehicles(){

return vehicles;

}

}

publicstaticvoid main(String[] args){

Truck<Wheel> truckWithWheel =new Truck<Wheel>();

truckWithWheel.addPart(new Wheel());

// This is needed to hide implementation.

VehicleParc<Vehicle><Part>> vp =new VehicleParc<Vehicle><Part>>();

// (1) This cannot be compiled, type cannot be infered to Vehicle<Part>!?!?

vp.addVehicle(truckWithWheel);

Vehicle<Wheel> vehicleWithWheel = truckWithWheel;

// (2) This cannot be compiled, type cannot be infered to Vehicle<Part>!?!?

vp.addVehicle(vehicleWithWheel);

Truck<Part> truckWithPart =new Truck<Part>();

Collection<Wheel> parts = truckWithWheel.getParts();

for (Wheel part : parts){

truckWithPart.addPart(part);

}

// This works! Type can be inferred to Vehicle<Part>.

vp.addVehicle(truckWithPart);

}

staticclass Wheelimplements Part{

private String part;

Wheel(){

this.part ="Wheel";

}

publicvoid info(){

System.out.println(part);

}

}

staticclass Truck<TPextends Part>implements Vehicle<TP>{

private Collection<TP> parts =new ArrayList<TP>();

publicvoid addPart(TP part){

parts.add(part);

}

public Collection<TP> getParts(){

return parts;

}

}

}

[4459 byte] By [jhujola] at [2007-11-26 13:22:32]
# 1

there's something really fundamentally wrong with your model. why parameterize vehicles by part? don't trucks always have wheels? what if it has wheels and brakes? re-think what you're doing and throw this away. trying to pick through everything refactoring it will give you a migraine, trust me. and it still won't work

georgemca at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 2
George,thx for your input on the design!I thought everybody understood that this is an example and not the real code ;)Try to stick to the issues I'm having.Thx,J
jhujola at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 3
It does not work, because neither a Truck<Wheel> nor a Vehicle<Wheel> is a Vehicle<Part>. They are Vehicle<? extends Part>.
stefan.schulza at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 4

just one question: if Part, Vehicle and VehicleParc are all interfaces, why do you use extends on your generic declaration, instead of just typing the Interface as the generic type? Isn't a interface sufficiently "generic"per si? I tried this:

import java.util.ArrayList;

import java.util.Collection;

public class GenericsTest {

static interface Part {

void info();

}

static interface Vehicle<Part> {

void addPart(Part part);

Collection<Part> getParts();

}

static class VehicleParc<Vehicle> {

private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();

void addVehicle(Vehicle vehicle) {

vehicles.add(vehicle);

}

Collection<Vehicle> getVehicles() {

return vehicles;

}

}

public static void main(String[] args) {

Truck<Wheel> truckWithWheel = new Truck<Wheel>();

truckWithWheel.addPart(new Wheel());

// This is needed to hide implementation.

VehicleParc<Vehicle> vp = new VehicleParc<Vehicle>();

// This works! Type can be inferred to Vehicle<Part>.Vehicle<Part>!?!?

vp.addVehicle(truckWithWheel);

Vehicle<Wheel> vehicleWithWheel = truckWithWheel;

// This works! Type can be inferred to Vehicle<Part>.Vehicle<Part>!?!?

vp.addVehicle(vehicleWithWheel);

Truck<Part> truckWithPart = new Truck<Part>();

Collection<Wheel> parts = truckWithWheel.getParts();

for (Wheel part : parts) {

truckWithPart.addPart(part);

}

// This works! Type can be inferred to Vehicle<Part>.

vp.addVehicle(truckWithPart);

}

static class Wheel implements Part {

private String part;

Wheel() {

this.part = "Wheel";

}

public void info() {

System.out.println(part);

}

}

static class Truck<TP extends Part> implements Vehicle<TP> {

private Collection<TP> parts = new ArrayList<TP>();

public void addPart(TP part) {

parts.add(part);

}

public Collection<TP> getParts() {

return parts;

}

}

}

and it compiles, I just don't understand the warning of the shadowing of Part and Vehicle.

With the extends declaration, you are saying that for instance truck has some part that implements Part, but java cannot ensure that the implementing class is the same must be the same across the same object, for the same generic. If that weren't true, you could do this:

Truck<Part> truckWithWheel = new Truck<Wheel>();//doesn't compile here

truckWithWheel.addPart(new Wheel());

truckWithWheel.addPart(new Engine());

but you can do this:

Truck<Part> truckWithWheel = new Truck<Part>();

truckWithWheel.addPart(new Wheel());

truckWithWheel.addPart(new Engine());

because you guarantee that there is always some Part in Truck, and not a subclass of part.

Note: my examples are from my source code, as you can see I removed the extends

Other note: I can be completely wrong of course... hehehe anyway that's my .2€

mpinga at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 5

> and it compiles, I just don't understand the warning

of the shadowing of Part and Vehicle.

You did not write down the Warnings, so I guess it's about using raw types, as you use the parametrized Vehicle class in a raw fashion.

> because you guarantee that there is always some Part

> in Truck, and not a subclass of part.

You just added subclass's instances to the list. You mean, one can guarantee, that elements of the list implement Part.

Message was edited by:

stefan.schulz

Corrected quotes.

stefan.schulza at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 6

> just one question: if Part, Vehicle and VehicleParc

> are all interfaces, why do you use extends on

> your generic declaration, instead of just typing the

> Interface as the generic type? Isn't a interface

> sufficiently "generic"per si? I tried this:

>

You're right there is no need for that extends! I got carried over

to much in willing to generify everything (just learning generics :)

> With the extends declaration, you are saying that for

> instance truck has some part that implements Part,

> but java cannot ensure that the implementing class is

> the same must be the same across the same object, for

> the same generic. If that weren't true, you could do

> this:

> > Truck<Part> truckWithWheel = new

> Truck<Wheel>();//doesn't compile here

> truckWithWheel.addPart(new Wheel());

> truckWithWheel.addPart(new Engine());

>

>

The ? extends SomeType is really not intuitive for me. I'll avoid these wildcard instantiation (as it's called in the docs I read) for now.

> > Truck<Part> truckWithWheel = new Truck<Part>();

> truckWithWheel.addPart(new Wheel());

> truckWithWheel.addPart(new Engine());

>

> because you guarantee that there is always some Part

> in Truck, and not a subclass of part.

This makes more sense and that's what I want at the end.

Stefan and Mping thx,

J

jhujola at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 7

Allright, I came up with this, which is really what I wanted to achieve somehow.

Basically I wanted to keep the real type of the parts of a vehicle along the implementation to further process the data before giving it up to the client, without compromising the public interface, which should just care about Vehicle and Part.

By using Vehicle<P extends Part>, VehicleParc<V extends Vehicle> and bounded wildcard instantiation (? extends SomeType) I can achieve the abstraction I wanted.

Here is the code (of course for learning purpose).

If one has a shorter or better solution, please don't hesitate!

J

import java.util.ArrayList;

import java.util.Collection;

public class GenericsTest {

public static abstract class Part {

protected String part;

public void info() {

System.out.println(part);

}

}

public static abstract class Vehicle<P extends Part> {

protected String type;

private Collection<P> parts = new ArrayList<P>();

public void printType() {

System.out.println(type);

}

public void addPart(P part) {

parts.add(part);

}

public Collection<P> getParts() {

return parts;

}

}

public static class VehicleParc<V extends Vehicle> {

private Collection<V> vehicles = new ArrayList<V>();

void addVehicle(V vehicle) {

vehicles.add(vehicle);

}

Collection<V> getVehicles() {

return vehicles;

}

}

public static void main(String[] args) {

VehicleParc<? extends Vehicle<? extends Part>> vp;

vp = getParcOfTrucksWithWheels();

showParcInfo(vp);

vp = getParcOfAutosWithEngines();

showParcInfo(vp);

}

public static <V extends Vehicle><? extends Part>> void showParcInfo(VehicleParc<V> vp) {

System.out.println("\nShow info");

Collection<V> vehicles = vp.getVehicles();

for (V v : vehicles) {

v.printType();

Collection<? extends Part> parts = v.getParts();

for (Part part : parts) {

part.info();

}

}

System.out.println("Done\n");

}

public static VehicleParc<? extends Vehicle<? extends Part>> getParcOfTrucksWithWheels() {

Truck<Wheel> truckWithWheel = new Truck<Wheel>();

truckWithWheel.addPart(new Wheel());

truckWithWheel.addPart(new Wheel());

truckWithWheel.addPart(new Wheel());

truckWithWheel.addPart(new Wheel());

VehicleParc<Truck><Wheel>> vp = new VehicleParc<Truck><Wheel>>();

vp.addVehicle(truckWithWheel);

System.out.println("\nImplementation checking or initializing stuff on real type");

// Now I can retrieve Wheel information to do something!

Collection<Truck><Wheel>> vehicles = vp.getVehicles();

for (Truck<Wheel> vehicle : vehicles) {

Collection<Wheel> wheels = vehicle.getParts();

for (Wheel wheel : wheels) {

wheel.checkPressure();

}

}

System.out.println("Done\n");

return vp;

}

public static VehicleParc<? extends Vehicle<? extends Part>> getParcOfAutosWithEngines() {

Auto<Engine> auto = new Auto<Engine>();

auto.addPart(new Engine());

VehicleParc<Auto><Engine>> vp = new VehicleParc<Auto><Engine>>();

vp.addVehicle(auto);

System.out.println("\nImplementation checking or initializing stuff on real type");

// Same here, I can retrieve Engine information!

Collection<Auto><Engine>> vehicles = vp.getVehicles();

for (Auto<Engine> vehicle : vehicles) {

Collection<Engine> engines = vehicle.getParts();

for (Engine engine : engines) {

engine.start();

}

}

System.out.println("Done\n");

return vp;

}

private static class Wheel extends Part {

private static int n = 0;

Wheel() {

this.part = "Wheel " + n++;

}

void checkPressure() {

System.out.println("Check pressure " + part);

}

}

private static class Engine extends Part {

private static int n = 0;

Engine() {

part = "Engine " + n++;

}

void start() {

System.out.println("Starting " + part);

}

}

private static class Truck<P extends Part> extends Vehicle<P> {

private static int n = 0;

Truck() {

type = "Truck " + n++;

}

void checkTruckStuff() {

System.out.println("Check " + type);

}

}

private static class Auto<P extends Part> extends Vehicle<P> {

private static int n = 0;

Auto() {

type = "Auto " + n++;

}

void seatAutoStuff() {

System.out.println("Seat " + type);

}

}

}

jhujola at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 8

I do not understand why classes Auto and Truck must themselves have "P extends Part" restriction when they are extending Vehicle which has the same type check (P extends Part). So wouldn't this render the type checking useless? Doesn't this alone suffice:

private static class Truck<P> extends Vehicle<P> { ... }

aria_kokoschkaa at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 9
Hey Aria :)Truck has to define P extends Part as a Vehicle only allows types that extend Part as Type Argument. Truck does not inherit the definition of P (P is a new, Truck-owned Type Parameter, in no way related to the P in Vehicle).
stefan.schulza at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...
# 10

Thanks Stefan for clarifying that. Every time I think I've got all the aspects of generics down, something new pops up.

Now, from the language design point of view, wouldn't it be more practical for the superclass to delegate such type restriction to its children? For example, in pre-generic Java, when a superclass implements an interface, its subclasses also are considered to have that type (the interface that is).

On the other hand, before the introduction of generics, concepts of type and class were basically interchangeable. List<Number> and List<Integer> are different types but the same class. So the class actually corresponds to two or more types. I just have a hard time wrapping my mind around these concepts...

*pray*

aria_kokoschkaa at 2007-7-7 17:53:39 > top of Java-index,Core,Core APIs...