Subtyping and Subclasses in Software Design

undefined
CSE 331
Software Design & Implementation
Dan Grossman
Spring 2015
Subtypes and Subclasses
(Based on slides by Mike Ernst, Dan Grossman, David Notkin, Hal Perkins)
What is subtyping?
Sometimes “
every B is an A
Example: In a library database:
Every book is a library holding
Every CD is a library holding
Subtyping expresses this
B is a subtype of A
 
means:
   “every object that satisfies the rules for a B
    also satisfies the rules for an A”
Goal: code written using A's specification operates correctly even if
given a B
Plus:  clarify design, share tests, (sometimes) share code
2
CSE331 Spring 2015
Subtypes are substitutable
S
u
b
t
y
p
e
s
 
a
r
e
 
s
u
b
s
t
i
t
u
t
a
b
l
e
 
f
o
r
 
s
u
p
e
r
t
y
p
e
s
Instances of subtype won't surprise client by failing to satisfy
the supertype's specification
Instances of subtype won't surprise client by having more
expectations than the supertype's specification
W
e
 
s
a
y
 
t
h
a
t
 
B
 
i
s
 
a
 
t
r
u
e
 
s
u
b
t
y
p
e
 
o
f
 
A
 
i
f
 
B
 
h
a
s
 
a
 
s
t
r
o
n
g
e
r
s
p
e
c
i
f
i
c
a
t
i
o
n
 
t
h
a
n
 
A
T
h
i
s
 
i
s
 
n
o
t
 
t
h
e
 
s
a
m
e
 
a
s
 
a
 
J
a
v
a
 
s
u
b
t
y
p
e
Java subtypes that are not true subtypes are 
confusing
 and
dangerous
But unfortunately common poor-design 
3
CSE331 Spring 2015
Subtyping vs. subclassing
Substitution (
subtype
) — a 
specification 
notion
B is a subtype of A iff an object of B can masquerade as an
object of A in any context
About satisfiability (behavior of a B is a subset of A’s spec)
Inheritance (
subclass
) — an 
implementation
 notion
Factor out repeated code
To create a new class, write only the differences
Java purposely merges these notions for classes:
Every subclass is a Java subtype
But not necessarily a true subtype
4
CSE331 Spring 2015
Inheritance makes adding functionality easy
Suppose we run a web store with a class for 
products…
class 
Product
 {
    private String 
title
;
    private String 
description
;
    private int 
price
; 
// in cents
    public int 
getPrice
() {
        return price;
     }
    public int 
getTax
() {
        return (int)(getPrice() * 0.096);
    }
}
... and we need a class for 
products that are on sale
5
CSE331 Spring 2015
We know: don’t copy code!
We would never dream of cutting and pasting like this:
class 
Sale
Product
 {
    private String 
title
;
    private String 
description
;
    private int 
price
; 
// in cents
    private float factor;
    public int 
getPrice
() {
       return 
(int)(
price
*factor)
;
    }
    
public int 
getTax
() {
        return (int)(getPrice() * 0.096);
    }
}
6
CSE331 Spring 2015
Inheritance makes small extensions small
Much better:
class 
SaleProduct 
extends Product 
{
    private float 
factor
;
    public int 
getPrice
() {
      return (int)(super.getPrice()*factor);
    }
}
7
CSE331 Spring 2015
Benefits of subclassing & inheritance
Don’t repeat unchanged fields and methods
In implementation
Simpler maintenance:  fix bugs once
In specification
Clients who understand the superclass specification need
only study novel parts of the subclass
Modularity:  can ignore private fields and methods of
superclass (if properly defined)
Differences not buried under mass of similarities
Ability to substitute new implementations
No client code changes required to use new subclasses
8
CSE331 Spring 2015
Subclassing can be misused
Poor planning can lead to a muddled 
class hierarchy
Relationships may not match untutored intuition
Poor design can produce subclasses that depend on many
implementation details of superclasses
Changes in superclasses can break subclasses
“fragile base class problem”
Subtyping and implementation inheritance are orthogonal!
Subclassing gives you both
Sometimes you want just one
Interfaces
: subtyping without inheritance [see also section]
Composition
: use implementation without subtyping
Can seem less convenient, but often better long-term
9
CSE331 Spring 2015
Is every square a rectangle?
 
interface 
Rectangle
 {
  
// effects: fits shape to given size:
  //          this
post
.width = w, this
post
.height = h
  void 
setSize
(int 
w
, int 
h
);
}
interface 
Square
 
extends Rectangle
 
{…}
Which is the best option for 
Square
’s 
setSize 
specification?
1.
 // requires: w = h
 
 
// effects: fits shape to given size
void 
setSize
(int 
w
, int 
h
);
2.
// effects: sets all edges to given size
void 
setSize
(int edgeLength);
3.
// effects:  sets this.width and this.height to w
void 
setSize
(int 
w
, int 
h
);
4.
 // effects: fits shape to given size
 
 
// throws BadSizeException if w != h
void 
setSize
(int 
w
, int 
h
) throws BadSizeException;
10
CSE331 Spring 2015
Square, Rectangle Unrelated (Subtypes)
Square
 is not a (true subtype of) 
Rectangle
:
Rectangle
s are expected to have a width and height
that can be mutated independently
Square
s violate that expectation, could surprise client
Rectangle
 is not a (true subtype of) 
Square
:
Square
s are expected to have equal widths and heights
Rectangle
s violate that expectation, could surprise client
Subtyping is not always intuitive
Benefit: it forces clear thinking and prevents errors
Solutions:
Make them unrelated (or siblings)
Make them immutable (!)
Recovers elementary-school intuition
11
CSE331 Spring 2015
Inappropriate subtyping in the JDK
 
class
 
Hashtable
<
K
,
V
>
 
{
 
  public void 
put
(K 
key
, V 
value
){…}
    
public V 
get
(K 
key
){…}
 
}
 
// Keys and values are strings.
 
class 
Properties 
extends Hashtable<Object,Object> 
{
 
   public void 
setProperty
(String 
key
, String 
val
) {
 
     put(key,val);
 
   }
 
   public String 
getProperty
(String 
key
) {
 
     return (String)get(key);
 
   }
 
}
12
CSE331 Spring 2015
Properties 
p
 = new Properties();
Hashtable 
tbl
 = p;
tbl.put("One", 1);
p.getProperty("One"); 
// crash!
Violation of rep invariant
Properties
 class has a simple rep invariant:
Keys and values are 
String
s
But client can treat 
Properties
 as a 
Hashtable
Can put in arbitrary content, break rep invariant
From Javadoc:
Because Properties inherits from Hashtable, the put and putAll
methods can be applied to a Properties object. ... If the store or
save method is called on a "compromised" Properties object
that contains a non-String key or value, 
the call will fail
.
13
CSE331 Spring 2015
Solution 1:  Generics
Bad choice:
class
 
Properties
 
extends Hashtable<
Object
,
Object
> {
}
Better choice:
class
 
Properties
 
extends Hashtable<
String
,
String
> {
}
JDK designers deliberately didn’t do this.  Why?
Backward-compatibility (Java didn’t used to have generics)
Postpone talking about generics: upcoming lecture
14
CSE331 Spring 2015
Solution 2:  Composition
class 
Properties
 {  
   private Hashtable<Object, Object> 
hashtable
;
   public void 
setProperty
(String 
key
, String 
value
) {
      hashtable.put(key,value);
   }
   public String 
getProperty
(String 
key
) {
      return (String) hashtable.get(key);
   }
}
15
CSE331 Spring 2015
Substitution principle for classes
If B is a subtype of A, a B can 
always be substituted
 for an A
Any property guaranteed by A must be guaranteed by B
Anything provable about an A is provable about a B
If an instance of subtype is treated purely as supertype (only
supertype methods/fields used), then the result should be
consistent with an object of the supertype being manipulated
B is 
permitted to strengthen
 properties and add properties
Fine to add new methods (that preserve invariants)
An overriding method must have a stronger (or equal) spec
B is 
not permitted to weaken
 a  spec
No method removal
No overriding method with a weaker spec
16
CSE331 Spring 2015
Substitution principle for methods
Constraints on methods
For each supertype method, subtype must have such a method
Could be inherited or overridden
Each overriding
 
method must 
strengthen
 (or match) the spec:
Ask nothing extra of client (“weaker precondition”)
Requires
 clause is at most as strict as in supertype’s method
Guarantee at least as much (“stronger postcondition”)
Effects
 clause is at least as strict as in the supertype method
No new entries in 
modifies
 clause
Promise more (or the same) in 
returns
 clause
Throws
 clause must indicate fewer (or same) possible
exception types
17
CSE331 Spring 2015
Spec strengthening: argument/result types
Method 
inputs
:
Argument types in A’s foo may be
replaced with supertypes in B’s foo
(“contravariance”)
Places no extra demand on the clients
But Java does not have such overriding
(Why?)
Method 
results
:
Result type of A’s foo may be replaced by
a subtype in B’s foo (“covariance”)
No new exceptions (for values in the domain)
Existing exceptions can be replaced with subtypes
   
 
(None of this violates what client can rely on)
18
CSE331 Spring 2015
Substitution exercise
Suppose we have a method which, when given one product,
recommends another:
     
class Product {
    Product 
recommend
(
Product 
ref
);
   }
Which of these are possible forms of this method in 
SaleProduct
(a true subtype of 
Product
)?
    Product 
recommend
(
SaleProduct 
ref
);
    SaleProduct 
recommend
(
Product 
ref
);
  
    Product 
recommend
(Object 
ref
);
    Product 
recommend
(
Product 
ref
)
         throws NoSaleException
;
 
 
 
// OK
 
// OK, 
but
 is Java
   overloading
 
// bad
 
// bad
19
CSE331 Spring 2015
Java subtyping
Java types:
Defined by classes, interfaces, primitives
J
a
v
a
 
s
u
b
t
y
p
i
n
g
 
s
t
e
m
s
 
f
r
o
m
 
B
 
e
x
t
e
n
d
s
 
A
 
 
a
n
d
B
 
i
m
p
l
e
m
e
n
t
s
 
A
 
 
d
e
c
l
a
r
a
t
i
o
n
s
In a Java subtype, each corresponding method has:
Same argument types
If different, 
overloading
:  unrelated methods
Compatible (covariant) return types
A (somewhat) recent language feature, not reflected in
(e.g.) 
clone
No additional declared exceptions
20
CSE331 Spring 2015
Java subtyping guarantees
A variable’s run-time type (i.e., the class of its run-time value) is a
Java subtype of its declared type
Object 
o
 = new Date(); 
// OK
Date 
d
 = new Object(); 
// compile-time error
If a variable of 
declared (compile-time) 
type T1 holds a
reference to an object of 
actual
 (
runtime) 
type T2, then T2 must
be a Java subtype of T1
Corollaries:
Objects always have implementations of the methods
specified by their declared type
If
 all subtypes are true subtypes, then all objects meet the
specification of their declared type
Rules out a huge class of bugs
21
CSE331 Spring 2015
Inheritance can break encapsulation
public class 
InstrumentedHashSet
<
E
>
                          
extends HashSet<E> {
  
private int addCount = 0;  
// count # insertions
  public 
InstrumentedHashSet
(Collection<? extends E> 
c
){
     super(c);
  }
  public boolean 
add
(E 
o
) {
     
addCount++;
     
return super.add(o);
  }
  public boolean 
addAll
(Collection<? extends E> 
c
) {
     
addCount += c.size();
     return super.addAll(c);
  }
  public int getAddCount() { return addCount; }
}
22
CSE331 Spring 2015
Dependence on implementation
 
What does this code print?
    
InstrumentedHashSet<String> 
s
 =
         new InstrumentedHashSet<String>();
  System.out.println(s.getAddCount());
  s.addAll(Arrays.asList("CSE", "331"));
  System.out.println(s.getAddCount());
 
Answer 
depends on
 
implementation
 of 
addAll
 in 
HashSet
Different implementations may behave differently!
If  
HashSet
’s 
addAll
 calls 
add
, then
 
double-counting
AbstractCollection
’s 
addAll
 specification:
“Adds all of the elements in the specified collection to this
collection.”
Does not specify whether it calls 
add
Lesson:  Subclassing often requires 
designing for extension
 
 
// 0
 
// 4?!
23
CSE331 Spring 2015
Solutions
1.
Change spec of 
HashSet
Indicate all self-calls
Less flexibility for implementers of specification
2.
Avoid spec ambiguity by avoiding self-calls
a)
“Re-implement” methods such as 
addAll
Requires re-implementing methods
b)
Use a wrapper
No longer a subtype (unless an interface is handy)
Bad for callbacks, equality tests, etc.
24
CSE331 Spring 2015
Solution 2b:  composition
public class 
InstrumentedHashSet
<
E
> {
  private final HashSet<E> 
s
 = new HashSet<E>();
  private int 
addCount 
= 0;
  public 
InstrumentedHashSet
(Collection<? extends E> 
c
){
this.addAll(c);
  }
  public boolean 
add
(E 
o
) {
     addCount++;   return s.add(o);
  }
  public boolean 
addAll
(Collection<? extends E> 
c
) {
     addCount += c.size();
     return s.addAll(c);
  }
  public int 
getAddCount
() {  return addCount; }
  // ... and every other method specified by HashSet<E>
}
The implementation
no longer matters
Delegate
25
CSE331 Spring 2015
Composition (wrappers, delegation)
Implementation 
reuse
 without 
inheritance
Easy to reason about; self-calls are irrelevant
Example of a “wrapper” class
Works around badly-designed / badly-specified classes
Disadvantages (may be worthwhile price to pay):
Does not preserve subtyping
Tedious to write (your IDE should help you)
May be hard to apply to callbacks, equality tests
26
CSE331 Spring 2015
Composition does not preserve subtyping
InstrumentedHashSet
 is not a 
HashSet
 anymore
So can't easily substitute it
It may be a true subtype of 
HashSet
But Java doesn't know that!
Java requires declared relationships
Not enough just to meet specification
Interfaces to the rescue
Can declare that we implement interface 
Set
If such an interface exists
27
CSE331 Spring 2015
Interfaces reintroduce Java subtyping
public class 
InstrumentedHashSet
<
E
> 
implements Set<E>
{
  private final Set<E> 
s
 = new HashSet<E>();
  private int 
addCount 
= 0;
  public 
InstrumentedHashSet
(Collection<? extends E> 
c
){
this.addAll(c);
  }
  public boolean 
add
(E 
o
) {
      addCount++;
      return s.add(o);
  }
  public boolean 
addAll
(Collection<? extends E> 
c
) {
      addCount += c.size();
      return s.addAll(c);
  }
  public int 
getAddCount
() {  return addCount; }
  // ... and every other method specified by Set<E>
}
Avoid encoding
implementation details
What’s bad  about this constructor?
InstrumentedHashSet(Set<E> 
s
) {
  this.s = s;
  addCount = s.size();
}
28
CSE331 Spring 2015
Interfaces and abstract classes
 
Provide 
interfaces
 for your functionality
Client code to interfaces rather than concrete classes
Allows different implementations later
Facilitates composition, wrapper classes
Basis of lots of useful, clever techniques
We'll see more of these later
 
Consider also providing helper/template 
abstract classes
Can minimize number of methods that new implementation
must provide
Makes writing new implementations much easier
Not necessary to use them to implement an interface, so
retain freedom to create radically different implementations
that meet an interface
29
CSE331 Spring 2015
Java library interface/class example
// root interface of collection hierarchy
interface 
Collection
<
E
>
// skeletal implementation of Collection<E>
abstract class 
AbstractCollection
<
E
>
 
   
implements Collection<E>
// type of all ordered collections
interface 
List
<
E
> extends Collection<E>
// skeletal implementation of List<E>
abstract class 
AbstractList
<
E
>
   
extends AbstractCollection<E>
   
implements List<E>
// an old friend...
class 
ArrayList
<
E
> extends AbstractList<E>
30
CSE331 Spring 2015
Why interfaces instead of classes?
Java design decisions:
A class has exactly one superclass
A class may implement multiple interfaces
An interface may extend multiple interfaces
Observation:
Multiple superclasses are difficult to use and to implement
Multiple interfaces, single superclass gets most of the benefit
31
CSE331 Spring 2015
Pluses and minuses of inheritance
Inheritance is a powerful way to achieve code reuse
Inheritance can break encapsulation
A subclass may need to depend on unspecified details of the
implementation of its superclass
E.g., pattern of self-calls
Subclass may need to evolve in tandem with superclass
Okay within a package where implementation of both is
under control of same programmer
Authors of superclass should design and document self-use, to
simplify extension
Otherwise, avoid implementation inheritance and use
composition instead
32
CSE331 Spring 2015
Slide Note
Embed
Share

Subtyping in software design refers to the relationship between types where a subtype can be substituted for its supertype without affecting the correctness of the program. This concept ensures that objects of a subtype meet the specifications of its supertype. Subtypes should not surprise clients by deviating from the supertype's expectations. On the other hand, subclasses, a concept related to inheritance, help in adding new functionality by factoring out repeated code and writing only the differences. While Java merges these notions for classes, not all subclasses are necessarily true subtypes. Inheritance plays a crucial role in software design by making it easy to add functionality through subclassing.

  • Subtyping
  • Subclasses
  • Software Design
  • Inheritance
  • Java

Uploaded on Sep 28, 2024 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

E N D

Presentation Transcript


  1. CSE 331 Software Design & Implementation Dan Grossman Spring 2015 Subtypes and Subclasses (Based on slides by Mike Ernst, Dan Grossman, David Notkin, Hal Perkins)

  2. What is subtyping? LibraryHolding A Sometimes every B is an A Example: In a library database: Every book is a library holding Every CD is a library holding Book CD B Shape Subtyping expresses this B is a subtype of A means: every object that satisfies the rules for a B also satisfies the rules for an A Rhombus Circle Goal: code written using A's specification operates correctly even if given a B Plus: clarify design, share tests, (sometimes) share code CSE331 Spring 2015 2

  3. Subtypes are substitutable Subtypes are substitutable for supertypes Instances of subtype won't surprise client by failing to satisfy the supertype's specification Instances of subtype won't surprise client by having more expectations than the supertype's specification We say that B is a true subtypeof A if B has a stronger specification than A This is not the same as a Java subtype Java subtypes that are not true subtypes are confusing and dangerous But unfortunately common poor-design CSE331 Spring 2015 3

  4. Subtyping vs. subclassing Substitution (subtype) a specification notion B is a subtype of A iff an object of B can masquerade as an object of A in any context About satisfiability (behavior of a B is a subset of A s spec) Inheritance (subclass) an implementation notion Factor out repeated code To create a new class, write only the differences Java purposely merges these notions for classes: Every subclass is a Java subtype But not necessarily a true subtype CSE331 Spring 2015 4

  5. Inheritance makes adding functionality easy Suppose we run a web store with a class for products class Product { private String title; private String description; private int price; // in cents public int getPrice() { return price; } public int getTax() { return (int)(getPrice() * 0.096); } } ... and we need a class for products that are on sale CSE331 Spring 2015 5

  6. We know: dont copy code! We would never dream of cutting and pasting like this: class SaleProduct { private String title; private String description; private int price; // in cents private float factor; public int getPrice() { return (int)(price*factor); } public int getTax() { return (int)(getPrice() * 0.096); } } CSE331 Spring 2015 6

  7. Inheritance makes small extensions small Much better: class SaleProduct extends Product { private float factor; public int getPrice() { return (int)(super.getPrice()*factor); } } CSE331 Spring 2015 7

  8. Benefits of subclassing & inheritance Don t repeat unchanged fields and methods In implementation Simpler maintenance: fix bugs once In specification Clients who understand the superclass specification need only study novel parts of the subclass Modularity: can ignore private fields and methods of superclass (if properly defined) Differences not buried under mass of similarities Ability to substitute new implementations No client code changes required to use new subclasses CSE331 Spring 2015 8

  9. Subclassing can be misused Poor planning can lead to a muddled class hierarchy Relationships may not match untutored intuition Poor design can produce subclasses that depend on many implementation details of superclasses Changes in superclasses can break subclasses fragile base class problem Subtyping and implementation inheritance are orthogonal! Subclassing gives you both Sometimes you want just one Interfaces: subtyping without inheritance [see also section] Composition: use implementation without subtyping Can seem less convenient, but often better long-term CSE331 Spring 2015 9

  10. Is every square a rectangle? interface Rectangle { // effects: fits shape to given size: // thispost.width = w, thispost.height = h void setSize(int w, int h); } interface Square extends Rectangle { } Which is the best option for Square s setSize specification? 1. // requires: w = h // effects: fits shape to given size void setSize(int w, int h); 2.// effects: sets all edges to given size void setSize(int edgeLength); 3.// effects: sets this.width and this.height to w void setSize(int w, int h); 4. // effects: fits shape to given size // throws BadSizeException if w != h void setSize(int w, int h) throws BadSizeException; CSE331 Spring 2015 10

  11. Square, Rectangle Unrelated (Subtypes) Rectangle Square is not a (true subtype of) Rectangle: Rectangles are expected to have a width and height that can be mutated independently Squares violate that expectation, could surprise client Square Rectangle is not a (true subtype of) Square: Squares are expected to have equal widths and heights Rectangles violate that expectation, could surprise client Square Subtyping is not always intuitive Benefit: it forces clear thinking and prevents errors Rectangle Shape Solutions: Make them unrelated (or siblings) Make them immutable (!) Recovers elementary-school intuition Square Rectangle CSE331 Spring 2015 11

  12. Inappropriate subtyping in the JDK class Hashtable<K,V> { public void put(K key, V value){ } public V get(K key){ } } // Keys and values are strings. class Properties extends Hashtable<Object,Object> { public void setProperty(String key, String val) { put(key,val); } public String getProperty(String key) { return (String)get(key); } } Properties p = new Properties(); Hashtable tbl = p; tbl.put("One", 1); p.getProperty("One"); // crash! CSE331 Spring 2015 12

  13. Violation of rep invariant Properties class has a simple rep invariant: Keys and values are Strings But client can treat Properties as a Hashtable Can put in arbitrary content, break rep invariant From Javadoc: Because Properties inherits from Hashtable, the put and putAll methods can be applied to a Properties object. ... If the store or save method is called on a "compromised" Properties object that contains a non-String key or value, the call will fail. CSE331 Spring 2015 13

  14. Solution 1: Generics Bad choice: class Properties extends Hashtable<Object,Object> { } Better choice: class Properties extends Hashtable<String,String> { } JDK designers deliberately didn t do this. Why? Backward-compatibility (Java didn t used to have generics) Postpone talking about generics: upcoming lecture CSE331 Spring 2015 14

  15. Solution 2: Composition class Properties { private Hashtable<Object, Object> hashtable; public void setProperty(String key, String value) { hashtable.put(key,value); } public String getProperty(String key) { return (String) hashtable.get(key); } } CSE331 Spring 2015 15

  16. Substitution principle for classes If B is a subtype of A, a B can always be substituted for an A Any property guaranteed by A must be guaranteed by B Anything provable about an A is provable about a B If an instance of subtype is treated purely as supertype (only supertype methods/fields used), then the result should be consistent with an object of the supertype being manipulated B is permitted to strengthen properties and add properties Fine to add new methods (that preserve invariants) An overriding method must have a stronger (or equal) spec B is not permitted to weaken a spec No method removal No overriding method with a weaker spec CSE331 Spring 2015 16

  17. Substitution principle for methods Constraints on methods For each supertype method, subtype must have such a method Could be inherited or overridden Each overriding method must strengthen (or match) the spec: Ask nothing extra of client ( weaker precondition ) Requires clause is at most as strict as in supertype s method Guarantee at least as much ( stronger postcondition ) Effects clause is at least as strict as in the supertype method No new entries in modifies clause Promise more (or the same) in returns clause Throws clause must indicate fewer (or same) possible exception types CSE331 Spring 2015 17

  18. Spec strengthening: argument/result types A Method inputs: Argument types in A s foo may be replaced with supertypes in B s foo ( contravariance ) Places no extra demand on the clients But Java does not have such overriding (Why?) Method results: Result type of A s foo may be replaced by a subtype in B s foo ( covariance ) No new exceptions (for values in the domain) Existing exceptions can be replaced with subtypes (None of this violates what client can rely on) LibraryHolding B Book CD Shape Circle Rhombus CSE331 Spring 2015 18

  19. Substitution exercise Suppose we have a method which, when given one product, recommends another: class Product { Product recommend(Product ref); } Which of these are possible forms of this method in SaleProduct (a true subtype of Product)? Product recommend(SaleProduct ref); SaleProduct recommend(Product ref); Product recommend(Object ref); // bad // OK // OK, but is Java overloading Product recommend(Product ref) // bad throws NoSaleException; CSE331 Spring 2015 19

  20. Java subtyping Java types: Defined by classes, interfaces, primitives Java subtyping stems from B extends Aand B implements A declarations In a Java subtype, each corresponding method has: Same argument types If different, overloading: unrelated methods Compatible (covariant) return types A (somewhat) recent language feature, not reflected in (e.g.) clone No additional declared exceptions CSE331 Spring 2015 20

  21. Java subtyping guarantees A variable s run-time type (i.e., the class of its run-time value) is a Java subtype of its declared type Object o = new Date(); // OK Date d = new Object(); // compile-time error If a variable of declared (compile-time) type T1 holds a reference to an object of actual (runtime) type T2, then T2 must be a Java subtype of T1 Corollaries: Objects always have implementations of the methods specified by their declared type If all subtypes are true subtypes, then all objects meet the specification of their declared type Rules out a huge class of bugs CSE331 Spring 2015 21

  22. Inheritance can break encapsulation public class InstrumentedHashSet<E> private int addCount = 0; // count # insertions public InstrumentedHashSet(Collection<? extends E> c){ super(c); } public boolean add(E o) { addCount++; return super.add(o); } public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } } CSE331 Spring 2015 extends HashSet<E> { 22

  23. Dependence on implementation What does this code print? InstrumentedHashSet<String> s = new InstrumentedHashSet<String>(); System.out.println(s.getAddCount()); s.addAll(Arrays.asList("CSE", "331")); System.out.println(s.getAddCount()); // 0 // 4?! Answer depends onimplementation of addAll in HashSet Different implementations may behave differently! If HashSet saddAll calls add, then double-counting AbstractCollection saddAll specification: Adds all of the elements in the specified collection to this collection. Does not specify whether it calls add Lesson: Subclassing often requires designing for extension CSE331 Spring 2015 23

  24. Solutions 1. Change spec of HashSet Indicate all self-calls Less flexibility for implementers of specification 2. Avoid spec ambiguity by avoiding self-calls a) Re-implement methods such as addAll Requires re-implementing methods b) Use a wrapper No longer a subtype (unless an interface is handy) Bad for callbacks, equality tests, etc. CSE331 Spring 2015 24

  25. Solution 2b: composition Delegate public class InstrumentedHashSet<E> { private final HashSet<E> s = new HashSet<E>(); private int addCount = 0; public InstrumentedHashSet(Collection<? extends E> c){ this.addAll(c); } public boolean add(E o) { addCount++; return s.add(o); } public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return s.addAll(c); } public int getAddCount() { return addCount; } // ... and every other method specified by HashSet<E> } CSE331 Spring 2015 The implementation no longer matters 25

  26. Composition (wrappers, delegation) Implementation reuse without inheritance Easy to reason about; self-calls are irrelevant Example of a wrapper class Works around badly-designed / badly-specified classes Disadvantages (may be worthwhile price to pay): Does not preserve subtyping Tedious to write (your IDE should help you) May be hard to apply to callbacks, equality tests CSE331 Spring 2015 26

  27. Composition does not preserve subtyping InstrumentedHashSet is not a HashSet anymore So can't easily substitute it It may be a true subtype of HashSet But Java doesn't know that! Java requires declared relationships Not enough just to meet specification Interfaces to the rescue Can declare that we implement interface Set If such an interface exists CSE331 Spring 2015 27

  28. Avoid encoding implementation details Interfaces reintroduce Java subtyping public class InstrumentedHashSet<E> implements Set<E>{ private final Set<E> s = new HashSet<E>(); private int addCount = 0; public InstrumentedHashSet(Collection<? extends E> c){ this.addAll(c); } public boolean add(E o) { addCount++; return s.add(o); } public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return s.addAll(c); } public int getAddCount() { return addCount; } // ... and every other method specified by Set<E> } CSE331 Spring 2015 What s bad about this constructor? InstrumentedHashSet(Set<E> s) { this.s = s; addCount = s.size(); } 28

  29. Interfaces and abstract classes Provide interfaces for your functionality Client code to interfaces rather than concrete classes Allows different implementations later Facilitates composition, wrapper classes Basis of lots of useful, clever techniques We'll see more of these later Consider also providing helper/template abstract classes Can minimize number of methods that new implementation must provide Makes writing new implementations much easier Not necessary to use them to implement an interface, so retain freedom to create radically different implementations that meet an interface CSE331 Spring 2015 29

  30. Java library interface/class example // root interface of collection hierarchy interface Collection<E> // skeletal implementation of Collection<E> abstract class AbstractCollection<E> implements Collection<E> // type of all ordered collections interface List<E> extends Collection<E> // skeletal implementation of List<E> abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> // an old friend... class ArrayList<E> extends AbstractList<E> CSE331 Spring 2015 30

  31. Why interfaces instead of classes? Java design decisions: A class has exactly one superclass A class may implement multiple interfaces An interface may extend multiple interfaces Observation: Multiple superclasses are difficult to use and to implement Multiple interfaces, single superclass gets most of the benefit CSE331 Spring 2015 31

  32. Pluses and minuses of inheritance Inheritance is a powerful way to achieve code reuse Inheritance can break encapsulation A subclass may need to depend on unspecified details of the implementation of its superclass E.g., pattern of self-calls Subclass may need to evolve in tandem with superclass Okay within a package where implementation of both is under control of same programmer Authors of superclass should design and document self-use, to simplify extension Otherwise, avoid implementation inheritance and use composition instead CSE331 Spring 2015 32

More Related Content

giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#