In most extant languages, member [fields?] are treated fundamentally differently from methods, but does this HAVE to be the case?
Yes, it does.
Member fields are per-instance data. Data must actually exist somewhere in memory for every instance - there must be an address or sequence of addresses reserved for it. In Java, .NET, and many other OO languages, this data goes on the heap.
Methods are per-class data. They are pointers to a vtable. That's how methods get virtual dispatch. There is only actually one instance allocated, per class.
An interface is essentially a special type of class that only contains methods, no instance data. That's part of the definition of interface. If it contained any data, it wouldn't be an interface anymore, it would be an abstract class. Both of those choices are fine, of course, depending on what kind of design pattern you're trying to implement. But if you added instance data to interfaces, you'd take away everything that makes them interfaces.
OK, so why can't an interface method point to a member field? Because there's nothing at the class level to point to. Again, an interface is nothing but a method table. The compiler can only generate one table, and every method in it has to point to the same memory address. Thus it's impossible for interface methods to point to class fields, because the class field is a different address for every instance.
Perhaps the compiler could accept this anyway and generate a getter method for the interface to point to - but then that essentially becomes a property. If you want to know why Java doesn't have properties, well... that's a really long and tedious story that I'd rather not get into here. But if you're interested, you might want to have a look at other OO languages which do implement properties, such as C# or Delphi. The syntax is really easy:
public interface IFoo
{
int Bar { get; }
}
public class Foo : IFoo
{
public int Bar { get; set; }
}
Yep, that's all there is to it. Important: These are not fields, the "int Bar" in the Foo
class is an Auto Property, and the compiler is doing exactly what I just described above: automatically generating getters and setters (as well as the backing member field).
So to recap the answer to your question:
- Interfaces require methods because their entire purpose is not to contain any data;
- The reason that there is no syntactic sugar to make it easier for a class to implement an interface is simply that the Java designers do not will it - and by this point in time, backward compatibility is an issue. Language designers make these kinds of choices; you'll just have to deal with it, or switch.
Yes, you are. Generic wildcards enforce that the container contains "something, but we're not sure what" and raw types are just "the container contains objects".
So you can get some counterintuitive results,
List<?> wild = new ArrayList<String>();
List raw = new ArrayList<String>();
wild.add(1); // Compile time error
raw.add(1);
raw.add("Foo");
wild.add("string"); // Still a compile time error
Wild cards are represented as the same as raw
at run time, but they are typechecked to make sure you never do something silly, like adding random objects to it.
So if you don't care about type safety, then yes, you can go ahead and use List
, but for 3 characters, I'd just use the parameterized type.
Best Answer
Since no one else has answered the question, I think I'll give it a go myself. I'm going to have to get a bit philosophical.
Generic programming is all about abstracting over similar types, without the loss of type information (which is what happens with object-oriented value polymorphism). In order to do this, the types must necessarily share some sort of interface (a set of operations, not the OO term) that you can use.
In object-oriented languages, types satisfy an interface by virtue of classes. Each class has its own interface, defined as part of its type. Since all classes
List<T>
share the same interface, you can write code that works no matter whichT
you choose. Another way to impose an interface is an inheritance constraint, and although the two seem different, they are sort of similar if you think about it.In most object-oriented languages,
List<>
is not a proper type in itself. It has no methods, and thus has no interface. It is onlyList<T>
that has these things. Essentially, in more technical terms, the only types you can meaningfully abstract over are those with the kind*
. In order to make use of higher-kinded types in an object-oriented world, you have to phrase type constraints in a manner consistent with this restriction.For example, as mentioned in the comments, we can view
Option<>
andList<>
as "mappable", in the sense that if you have a function, you could convert anOption<T>
into anOption<S>
, or aList<T>
into aList<S>
. Remembering that classes cannot be used to abstract over higher-kinded types directly, we instead make an interface:And then we implement the interface in both
List<T>
andOption<T>
asIMappable<List<_>, T>
andIMappable<Option<_>, T>
respectively. What we've done, is using higher-kinded types to place constraints on the actual (non-higher-kinded) typesOption<T>
andList<T>
. This is how it's done in Scala, though of course Scala has features such as traits, type variables, and implicit parameters that make it more expressive.In other languages, it is possible to abstract over higher-kinded types directly. In Haskell, one of the highest authorities on type systems, we can phrase a type class for any type, even if it has a higher kind. For example,
This is a constraint placed directly on an (unspecified) type
mp
which takes one type parameter, and requires it be associated with the functionmap
that turns anmp<a>
into anmp<b>
. We can then write functions that constrain higher-kinded types byMappable
just like in object-oriented languages you could place an inheritance constraint. Well, sort of.To sum things up, your ability to make use of higher-kinded types depends on your ability to constrain them or to use them as part of type constraints.