Polymorphic (Generic) Programming in Java

Similar documents
Programming Language Concepts: Lecture 5

Programming Language Concepts: Lecture 5

Programming Language Concepts: Lecture 7

A declaration may appear wherever a statement or expression is allowed. Limited scopes enhance readability.

Properties of an identifier (and the object it represents) may be set at

Chapter 5 Object-Oriented Programming

Exercise 8 Parametric polymorphism November 18, 2016

The list abstract data type defined a number of operations that all list-like objects ought to implement:

Java Object Oriented Design. CSC207 Fall 2014

Java: introduction to object-oriented features

C# Adds Useful Features

Short Notes of CS201

Index. Index. More information. block statements 66 y 107 Boolean 107 break 55, 68 built-in types 107

CS201 - Introduction to Programming Glossary By

CS-202 Introduction to Object Oriented Programming

Lecture Outline. Parametric Polymorphism and Java Generics. Polymorphism. Polymorphism

A Short Summary of Javali

Types. Type checking. Why Do We Need Type Systems? Types and Operations. What is a type? Consensus

Object-Oriented Design

Generalizing Collection Classes Using Generics with other Java 1.5 Features Integration of Generics with Previous Releases User built generic classes

COMSC-051 Java Programming Part 1. Part-Time Instructor: Joenil Mistal

Generics עזאם מרעי המחלקה למדעי המחשב אוניברסיטת בן-גוריון

Practice for Chapter 11

Introduction to Programming Using Java (98-388)

Abstract Classes. Abstract Classes a and Interfaces. Class Shape Hierarchy. Problem AND Requirements. Abstract Classes.

Lecture 5: Inheritance

What is Inheritance?

Object typing and subtypes

Agenda. Objects and classes Encapsulation and information hiding Documentation Packages

Generics עזאם מרעי המחלקה למדעי המחשב אוניברסיטת בן-גוריון

CH. 2 OBJECT-ORIENTED PROGRAMMING

Chapter 1 Getting Started

CSE Lecture 7: Polymorphism and generics 16 September Nate Nystrom UTA

Rules and syntax for inheritance. The boring stuff

More on Inheritance. Interfaces & Abstract Classes

M301: Software Systems & their Development. Unit 4: Inheritance, Composition and Polymorphism

4 CoffeeStrainer Virtues and Limitations

What about Object-Oriented Languages?

Chapter 14 Abstract Classes and Interfaces

25. Generic Programming

Parametric polymorphism and Generics

Data Abstraction. Hwansoo Han

OOPS Viva Questions. Object is termed as an instance of a class, and it has its own state, behavior and identity.

Inheritance. OOP components. Another Example. Is a Vs Has a. Virtual Destructor rule. Virtual Functions 4/13/2017

abstract binary class composition diamond Error Exception executable extends friend generic hash implementation implements

CSE1720. General Info Continuation of Chapter 9 Read Chapter 10 for next week. Second level Third level Fourth level Fifth level

C++ Inheritance and Encapsulation

Binghamton University. CS-140 Fall Dynamic Types

More About Objects. Zheng-Liang Lu Java Programming 255 / 282

Cpt S 122 Data Structures. Course Review Midterm Exam # 2

Object-Oriented Languages and Object-Oriented Design. Ghezzi&Jazayeri: OO Languages 1

Object Oriented Programming: Based on slides from Skrien Chapter 2

Object-Oriented Concepts and Design Principles

First IS-A Relationship: Inheritance

More On inheritance. What you can do in subclass regarding methods:

Argument Passing All primitive data types (int etc.) are passed by value and all reference types (arrays, strings, objects) are used through refs.

Lecture 13: Object orientation. Object oriented programming. Introduction. Object oriented programming. OO and ADT:s. Introduction

An Introduction to Subtyping

Instantiation of Template class

Topics Covered Thus Far. CMSC 330: Organization of Programming Languages. Language Features Covered Thus Far. Programming Languages Revisited

CSE 331. Generics (Parametric Polymorphism)

CSE 431S Type Checking. Washington University Spring 2013

CSE 143 Lecture 26. Advanced collection classes. (ADTs; abstract classes; inner classes; generics; iterators) read 11.1, 9.6, ,

Contents. I. Classes, Superclasses, and Subclasses. Topic 04 - Inheritance

Operational Semantics of Cool

Java for Programmers Course (equivalent to SL 275) 36 Contact Hours

Compiler Construction 2009/2010 Polymorphic Types and Generics

Programming Language Concepts: Lecture 6

ECE 3574: Dynamic Polymorphism using Inheritance

Advanced Programming - JAVA Lecture 4 OOP Concepts in JAVA PART II

CMSC 330: Organization of Programming Languages

1B1b Inheritance. Inheritance. Agenda. Subclass and Superclass. Superclass. Generalisation & Specialisation. Shapes and Squares. 1B1b Lecture Slides

COURSE 2 DESIGN PATTERNS

Building Java Programs

Software Paradigms (Lesson 3) Object-Oriented Paradigm (2)

ob-ject: to feel distaste for something Webster's Dictionary

Java 1.5 in a Nutshell

1.1. Annotations History Lesson - C/C++

Polymorphism and Interfaces. CGS 3416 Spring 2018

CSE 307: Principles of Programming Languages

CMSC 202 Section 010x Spring Justin Martineau, Tuesday 11:30am

6.005 Elements of Software Construction Fall 2008

Introduction to Object-Oriented Programming

Review: Object Diagrams for Inheritance. Type Conformance. Inheritance Structures. Car. Vehicle. Truck. Vehicle. conforms to Object

Absolute C++ Walter Savitch

Computer Science 2 Lecture 4 Inheritance: Trinidad Fruit Stand 02/15/2014 Revision : 1.7

More Relationships Between Classes

9 Working with the Java Class Library

Announcement. Agenda 7/31/2008. Polymorphism, Dynamic Binding and Interface. The class will continue on Tuesday, 12 th August

Comp 311 Principles of Programming Languages Lecture 21 Semantics of OO Languages. Corky Cartwright Mathias Ricken October 20, 2010

Casting -Allows a narrowing assignment by asking the Java compiler to "trust us"

CS/ENGRD 2110 FALL Lecture 6: Consequence of type, casting; function equals

Contents. Figures. Tables. Examples. Foreword. Preface. 1 Basics of Java Programming 1. xix. xxi. xxiii. xxvii. xxix

Java Design Goals. Lecture 32: Java. Java Original implementations slow! Exceptions & Subtyping. - void method readfiles() throws IOException {...}!

Harvard School of Engineering and Applied Sciences Computer Science 152

CSE 331 Software Design and Implementation. Lecture 14 Generics 2

Object Orientated Analysis and Design. Benjamin Kenwright

Operational Semantics. One-Slide Summary. Lecture Outline

Goals of the Lecture OO Programming Principles

What are the characteristics of Object Oriented programming language?

Transcription:

Polymorphic (Generic) Programming in Java We have used the fact that Java classes are arranged as a tree with the built in class Object at the root to write generic or polymorphic code such as the following function to find an element in an array: public int find (Object[] objarr, Object o){ int i; for (i = 0; i < objarr.length; i++){ if (objarr[i] == o) {return i; return (-1); What if we wanted to write a similar program to copy the contents of one array into another? A first attempt could be the following: public void arraycopy (Object[] src, Object[] tgt){ int i,limit; limit = Math.min(src.length,tgt.length); // No overflows! for (i = 0; i < limit; i++){ tgt[i] = src[i]; However, notice that it is not enough for the two arguments to be compatible with Object for this function to work correctly: we also need the type of the array tgt to be a supertype of the array src for the assignment to work. In other words, assuming that we have a class Ticket with ETicket as a subclass, the following call is legal Ticket[] ticketarr = new Ticket[10]; ETicket[] elecarr = new ETicket[10]; arraycopy(elecarr,ticketarr); because it is legal to assign ETicket objects to variables of type Ticket. converse call However, the arraycopy(ticketarr,elecarr); would be flagged as a type error at runtime. In general, it is desirable to catch all type errors at compile time, but this kind of error cannot be detected because we have no mechanism to express dependencies across types in polymorphic code. Let us look at another situation where our existing mechanism for typing is inadequate. Suppose we want to define a linked list that can hold values of any type. We could begin with a private class to store a node of the list described as follows: 1

private class Node { public Object data; public Node next; We can then define our list as follows: public class LinkedList{ private int size; private Node first; public LinkedList(){ first = null; public Object head(){ // return data at head of list if nonempty Object returnval = null; if (first!= null){ returnval = first.data; first = first.next; return returnval; public void insert(object newdata){ // code to insert newdata in list private class Node{ There are two points to note about this style of implementing a polymorphic list: 1. The implementation loses information about the original type of the element. All data is stored and returned as an Object. This means we have to cast the output of head even when it is clear what the return type is, as shown below: 2

LinkedList list = new LinkedList(); Ticket t1,t2; t1 = new Ticket(); list.insert(t1); t2 = (Ticket)(list.head()); // head() returns an Object 2. This implementation does not capture our intuitive understanding that the underlying type of the list is uniform. There is nothing to stop us from constructing a heterogeneous list as follows: LinkedList list = new LinkedList(); Ticket t = new Ticket(); Date d = new Date(); list.insert(t); list.insert(d); Once again, the bottleneck is our inability to specify dependency across types within the list: we would like to say that nodes can hold any type of data but the types across all nodes in a list are the same. One way of addressing these deficiencies is to introduce explicit type variables. This has been incorporated in Java relatively recently (version 1.5) under the name generics. 1 Generics in Java We can now introduce type parameters for class definitions by using type variables. For instance, here is how we could modify our list definition to fix an arbitrary but uniform type T for the data stored in the list. public class LinkedList<T>{ private int size; private Node first; public T head(){ T returnval; return returnval; 3

public void insert(t newdata){ // code to insert newdata in list private class Node { public T data; public Node next; The parameter T in angled brackets denotes a type variable for the class LinkedList. An instance of LinkedList would have T bound to a fixed class: for instance, LinkedList<Ticket> or LinkedList<Date>. All references to T inside the class definition are bound to the value of T used when the class was instantiated. Here is an example of how we would define and use the new type of linked list. LinkedList<Ticket> ticketlist = new LinkedList<Ticket>(); LinkedList<Date> datelist = new LinkedList<Date>(); Ticket t = new Ticket(); Date d = new Date(); ticketlist.insert(t); datelist.insert(d); When we invoke ticketlist.insert(t), the type of t is compared by the compiler to the instantiation of the type T used when defining ticketlist. In this case, since ticketlist was created as an object of type LinkedList<Ticket>, T is bound to Ticket and so it is legal to invoke ticketlist.insert(t) for a Ticket object t. On the other hand, ticketlist.insert(d) for a Date d would be a type error. It is important to note that this type compatibility can be checked at compile time. 1 Notice that all instances of T within the parameterized class definition are automatically bound once the class is created with a specific value of T. Thus, the return type T in the function head and the type T for the field data in the private class Node both get instantiated correctly to the actual type provided for T when the class is instantiated. 1 We are not constrained to use T to denote a type variable. We can use any name we want, but it is helpful to have a standard convention to avoid confusing type variable and normal data variables. 4

We can also define type parameters for functions within a class. For instance, suppose we want to rewrite arraycopy in a very strict form that insists that both the source and target arrays are of the same type. We could specify this as follows: public <T> void arraycopy (T[] src, T[] tgt){ int i,limit; limit = min(src.length,tgt.length); // No overflows! for (i = 0; i < limit; i++){ tgt[i] = src[i]; The type parameter comes before the return type. Since T is substituted uniformly by the same type value, both src and tgt must be arrays of the same type. It is important to note the difference between the definitions of arraycopy and head (from LinkedList). In the function head, we used a type variable T without defining it as part of the function definition. This is because the T that appears in head is derived from the parameter T supplied to the surrounding class. If we write, instead public <T> T head(){ T returnval; return returnval; then the type variable T referred to inside head is a new, private variable which hides the outer T and is completely independent. In other words, we should think of <T> like a logical quantifier with a natural scope. If we reuse <T> within the scope of another <T>, the outer T is hidden by the inner T. 2 Type dependencies and type variables For arraycopy, it is sufficient for the type of the target array tgt to be a supertype of the source array src. We can specify that the two arrays are of different types by using multiple type variables, say S and T. As a first step, we write public <S,T> void arraycopy (S[] src, T[] tgt){ This, however, does not define any constraints on the types assigned to S and T. We want S to be a subtype of T, so we can make our definition more precise by writing 2 : 2 In this context, Java does not distinguish between S implementing an interface T and S extending a class T. For both cases, we just write S extends T. 5

public <S extends T,T> void arraycopy (S[] src, T[] tgt){ As another example, suppose we want to relax our typing constraint for lists to allow a list of type T to store elements of any subtype of T. We could then define insert as follows: public class LinkedList<T>{ public <S extends T> void insert(s newdata){ // code to insert newdata in list Now we cannot be sure, in general, about the specific type of each element of the list. However, we do still know that all values in the list are compatible with type T. Thus, the definition and type of the function head remain the same as before: public T head(){ T returnval; return returnval; 3 Type variables and the class hierarchy For conventional types, we can lift the hierarchy on underlying types to more complex objects. For instance, if S is a subtype of T then S[] is also a subtype of T[]. This is crucial, for instance, to write the generic find function that we began with. public int find (Object[] objarr, Object o){ If we did not have that S[] is compatible with Object[] for every type S, this function would not work in the polymorphic manner that we expect. However, consider the following situation, where (as usual) ETicket is a subclass of Ticket. ETicket[] elecarr = new ETicket[10]; Ticket[] ticketarr = elecarr; // OK. ETicket[] is a // subtype of Ticket[] ticketarr[5] = new Ticket(); // Not OK. ticketarr[5] // refers to an ETicket! 6

This generates a type error at runtime. The compiler cannot find fault with the assignment ticketarr[5] = new Ticket(); because ticketarr is defined to be of type Ticket[]! With type variables, we can implement polymorphic functions without requiring this troublesome property that the class hierarchy on basic types is extended uniformly to more complex structures built on that type. Thus, for classes defined with type variables, we no longer inherit the hierarchy. For instance, though ETicket extends Ticket, LinkedList<ETicket> does not extend LinkedList<Ticket>. Suppose we want to write a function printlist that handles arbitrary lists. We might be tempted to write this as follows: public static void printlist(linkedlist<object> list){ However, since the hierarchy on base types is not inherited by LinkedList, LinkedList<Object> is not the most general type of LinkedList. Instead, if we want a function that works on all variants of LinkedList, we should introduce a type variable: public static <T> void printlist(linkedlist<t> list){ 4 Type variables and reflection The class Class whose objects represent information about Java classes is now a generic class that takes a type parameter. Thus, the type String is represented by an object of type Class<String>, type ETicket is represented by an object of type Class <ETicket> etc. Unfortunately, type variables cannot be used in all contextss where a type name is expected. As we have seen, type variables can be used to specify the types of arguments and return values to functions and also to specify the types of variables within a function or a class. However in expressions such as if (s instanceof Shape){ we cannot replace Shape by a type variable and write if (s instanceof T){ // T a type variable This means that the following version of the function classequal (see page 83 in the older notes) would not work: public static <S,T> boolean classequal(s o1, T o2){ return (o1 instanceof T) && (o2 instance of S); // Illegal! 7

However, we can fruitfully exploit the fact that Class now has a type parameter. Suppose we want to write a reflective function that takes as input a class and creates a new object of that type. In the normal setup, we would have written: public Object createinstance(class c){ return c.newinstance(); The return type of this function is Object because we cannot infer any information about the nature of the class c which is received as the argument. In the revised framework, we if c is the Class object corresponding to a class T, then it is actually of type Class<T>. Thus, we can rewrite this function as follows: public T createinstance(class<t> c){ return c.newinstance(); Now, notice that we are able to extract the underlying type T from c and use it to specify a more precise return value, thus avoiding the need to use a cast when calling this function. One important point about Java s parameterized classes is that they are more sophisticated than macros or templates. It is not correct to think of a definition like public class LinkedList<T>{ as a skeleton that is instantiated into a real Java class each time it is actually used with a concrete value of T. There is truly only one class LinkedList<T> and all specific instantiations of LinkedList<T> have the same underlying object. In other words, if we declare LinkedList<String> stringlist = new LinkedList<String>(); LinkedList<Integer> intlist = new LinkedList<Integer>(); then classequal(stringlist,intlist) returns true. This seems to have some unfortunate implications. For instance, suppose we write: LinkedList<String> sl = new LinkedList<String>(); LinkedList<String> newsl = createinstance(sl.getclass()); Since all instantiations LinkedList<T> are of the same type, the value returned by sl.getclass() is of the generic type LinkedList<T> and the Java compiler is unable to reconcile this with the specific type LinkedList<String> defined for newsl. (Try this for yourself and see.) 8