Interfaces
- An interface is like an abstract class with only methods
- big deal, hot spit, so what
- classes can implement multiple interfaces
- An interface describes what an object does, rather than how it’s implemented
- this is very, very useful for data structures
- there are many data structures which “do the same thing” but in different ways
- usually we don’t want the code that uses a data structure to care how it’s implemented!
- the same algorithm can be customized to use many different data structures
- A class that implements five tiny interfaces is much more flexible than one which extends one giant superclass!
- it can be used in many different situations, instead of being stuck into a rigid hierarchy
- you can write methods/classes which only need the minimum amount of information needed
- then you can pass instances of that class to them
Polymorphism
- Polymorphism is a programming tool for abstracting the specific details of a generalized process
- basically, you say one thing, and one of many things can happen
- there are many kinds of polymorphism such as subtype, ad-hoc, and parametric
- Java has facilities for all three (to some extent)
- Polymorphism is an important abstraction tool!
- It lets you write algorithms and data structures which are more flexible
- It lets you hide the details of operations and focus instead on the “what”
- Subtype polymorphism is that whole “virtual method” and “override” system
- A base type declares a method (maybe abstract as in an interface or abstract class)
- Subtypes can provide their own unique implementations of that method
- When calling the method on a base type reference, which function is called is determined at run-time - since it’s impossible to know at compile-time which type it refers to
- Ad-hoc polymorphism is the “real” term for function overloading
- multiple functions/methods with same name but different parameter signatures
- e.g.
void display(int)
, void display(float)
, void display(String)
- which function is called is determined at compile-time based on argument types
display(4)
vs display(5f)
vs display("hello")
- Parametric polymorphism is something new to you which Java implements with generics
Generics
- What is a generic?
- A way of writing a function (or type) which can operate on any type
- e.g.
<T> void display(T value)
(see Ex07Generic
)
<T>
is a type parameter
- this makes a
display
method that will work for any type
- in logical terms,
∀T∈types, you can call display(T)
- can call it as
display(4)
or display(5f)
or display("hello")
… or anything else: display(new Object())
; display('x')
etc etc etc.
- this way, you don’t have to write an infinite number of overloads!
- Java’s
Object
as a crappy version of generics
- Any value in Java can be put into an
Object
variable
- Primitive types (
int
, float
etc) get turned into their “classy” counterparts (Integer
, Float
etc)
- But this has a serious disadvantage: there is no way to check for correctness at compile time.
- Imagine an array of
Object
, where you expect everything to be an Integer
, but somehow a String
sneaks in. What happens?
- You get some kind of runtime exception.
- Maybe.
- If you’re lucky, and the problematic code runs, and it happens consistently, and that value gets accessed in a way that causes an exception, and and and and…
- The more things you can check at compile time, the fewer bugs your program can have.
- Compiler errors aren’t the compiler being picky.
- They are the compiler helping you to eliminate entire categories of bugs.
Generic classes and interfaces
- Simple generic classes
- Generics are a sort of “fill-in-the-blanks” function or type
- The
Pair<T>
example class (Ex08Pair
) shows this
- You can have a
Pair
of anything, like Pair<Integer>
or Pair<File>
or whatever
- When you instantiate a generic, you can write
new Pair<Whatever>()
- Or, in most cases, leave the
<>
empty: new Pair<>()
- This is type inference: Java figures out what type needs to go in the
<>
- Generic interfaces
- You can make interfaces generic too
- When a class implements a generic interface, you have to tell it what its type parameter is
- That might be another type, or the class itself, or the class’s own type parameter
Comparable<T>
is a very common generic interface: it means “I can be compared to another T”
- Other than the type parameter, generic interfaces are just like regular ones
- Implementing a generic interface
- The
Ex09OrderedPair
example shows a class which holds a pair of integers, and the pairs can be compared (ordered)
- Since the instances can be compared, you can do stuff like sort arrays of them
Type bounds
- “Raw” generics aren’t very powerful
- Since you don’t know anything about the generic type… you can’t do anything with it either.
- A generic type parameter is basically an Object reference, which, uh, you can’t do much with.
- You can print it out! But that’s about it.
- What we need is a way of saying “I want something generic, but which has certain properties”
- “Has certain properties” should make you think “hmm… interfaces”
- You can place bounds on generic type parameters
- You do this by saying which interface(s) the type parameter implements
- er, the syntax uses
extends
, for some reason.
- Have a look at
Ex10TypeBounds
- It’s the same
Pair<T>
class as before
- now we have this
biggestFirst
function, which has this type parameter: <T extends Comparable<T>>
- This says “I will work on any T, as long as it implements the Comparable interface.”
- This is a type bound: a restriction (bound) on the kinds of types it will accept
- Type bounds are super useful and you will see them allllll the time
- Okay that’s enough linguistic exposition; let’s see what we can use all this stuff for
ADTs
- ADT stands for Abstract Data Type
- These are the foundation of data structures
- A data structure consists of two parts: the data it holds, and what you can do to the data
- That matches pretty nicely to Java’s OOP model, doesn’t it?
- ADTs are abstract because they only specify the behaviors, not the implementation
- Top-down design
- Programming is about solving problems; computers are incidental in the solution
- When solving a problem, start with the what, not the how
- WHAT is the problem? WHAT information do we have? WHAT information do we need?
- WHAT data do we need to store? WHAT do we need to be able to do to it?
- The problem at hand decides what kind of ADT we need
- Solving problems
- We are making a shopping site and need a shopping cart
- WHAT does the shopping cart need to hold?
- WHAT do we need to be able to do to the shopping cart?
- We are making a mailing list application for sending automated emails to groups of users
- WHAT does the mailing list need to hold?
- WHAT do we need to be able to do with the mailing list?
- We are implementing
String
- WHAT data does it store?
- WHAT can we do to the string?
- The client code
- The data structure does the work of handling the data
- The client code is the code that uses the data structure
- And the less the client knows about HOW the data structure works, the better.
- The usual arguments are “flexibility” and “reusability”, which are absolutely true
- But I also add ease of development, debugging, and testing
- The less interconnected your code is, the easier it is to find and fix problems
- Also, the easier it is to understand your code
- Which is the whole point of programming.
- ADTs and Java
- ADTs are language-agnostic
- They’re like a mathematical concept that exists outside a single programming language
- But what do you think we will use to describe what something does, without describing how?
- And then classes will implement those interfaces.
- Collections
- The most common kind of ADT defines a collection of items
- Lists, sets, mappings, trees, graphs…
- We refer to these as collections, and so does Java
- Java has the Collections Framework which defines a bunch of built-in interfaces and classes
- We’ll be implementing our own versions of some of these, cause, well, you’re taking a class
- If you were taking a cooking class, you wouldn’t buy food from the store and claim that you made it