CS 61B: Lecture 15 - Exceptions, Generics, and Field Shadowing, Study notes of Data Structures and Algorithms

A part of the lecture notes for cs 61b at the university of california, berkeley. It covers the topics of exceptions, specifically the 'finally' keyword and exception constructors. Additionally, it introduces generics and their use in java, as well as field shadowing in subclasses. Useful for university students studying computer science, particularly those enrolled in cs 61b or similar courses.

Typology: Study notes

2010/2011

Uploaded on 10/29/2011

jokerxxx
jokerxxx 🇺🇸

4.3

(36)

330 documents

1 / 2

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
10/01/10
20:20:56 1
15
CS 61B: Lecture 15
Friday, October 1, 2010
Today’s reading: Sierra & Bates, pp. 189, 283.
EXCEPTIONS (continued)
==========
The "finally" keyword
---------------------
A finally clause can also be added to a "try."
try {
statementX;
return 1;
} catch (SomeException e) {
e.printStackTrace();
return 2;
} finally {
f.close();
return 3;
}
If the "try" statement begins to execute, the "finally" clause will be executed
at the end, no matter what happens. This example (which is contrived) always
returns 3; never 1 or 2.
If statementX causes a SomeException, the exception is caught, the "catch"
clause is executed, and then the "finally" clause is executed. If statementX
causes some other class of exception, the "finally" clause is executed
immediately, then the exception continues to propagate down the stack.
"finally" clauses are used to do things that need to be done in both normal and
exceptional circumstances. (Again, closing files is an example.)
In the example above, we’ve invoked the method "printStackTrace" on the
exception we caught. When an exception is created, it takes a snapshot of the
stack, which can be printed later.
It is possible for an exception to occur in a "catch" or "finally" clause. An
exception thrown in a "catch" clause will terminate the "catch" clause, but the
"finally" clause will still get executed before the exception goes on. An
exception thrown in a "finally" clause replaces the old exception, and
terminates the "finally" clause and the method immediately.
However...you can put a "try" clause inside a "catch" or "finally" clause,
thereby catching those exceptions as well.
Exception constructors
----------------------
By convention, most Throwables (including Exceptions) have two constructors.
One takes no parameters, and one takes an error message in the form of a
String.
class MyException extends Exception {
public MyException() { super(); }
public MyException(String s) { super(s); }
}
The error message will be printed if it propagates out of main(), and it can be
read by the Throwable.getMessage() method. The constructors usually call the
superclass constructors, which are defined in Throwable.
GENERICS
========
Suppose you’re using a list of Objects to store Strings. When you fetch a
String from the list, you have to cast it back to type "String" before you can
call the methods exclusive to Strings. If somehow an object that’s not a
String got into your list, the cast will throw an exception. It would be nice
to have the compiler enforce the restriction that nothing but Strings can ever
get into your list in the first place, so you can sleep at night knowing that
your family is safe from a terrible ClassCastException.
So Java offers _generics_, which allow you to declare general classes that
produce specialized objects. For example, you can create an SList for Strings
only, and another SList for Integers only, even though you only wrote one
SList class. To specify the class, SList takes a _type_parameter_.
class SListNode<T> {
T item;
public SListNode<T> next;
SListNode(T i, SListNode<T> n) {
item = i;
next = n;
}
}
public class SList<T> {
SListNode<T> head;
public void insertFront(T item) {
head = new SListNode<T>(item, head);
}
}
You can now create and use an SList of Strings as follows.
SList<String> l = new SList<String>();
l.insertFront("Hello");
Likewise, you can create an SList of Integers by using "SList<Integer>" in the
declaration and constructor.
The advantage of generics here is that the compiler will ensure at compile-
time that nothing but Strings can ever enter your SList<String>, so there will
be no nasty surprises at run time.
Generics are a complicated subject. Consider this to be a taste of them;
hardly a thorough treatment. Sun has a much longer tutorial at
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf .
Although Java generics are superficially similar to C++ templates, there’s a
crucial difference between them. In the example above, Java compiles bytecode
for only a single SList class. This SList bytecode can be used by all
different object types. It is the compiler, not the bytecode itself, that
enforces the fact that a particular SList object can only store objects of a
particular class. Conversely, C++ recompiles the SList methods for every type
that you instantiate SLists on. The disadvantage is that one class might turn
into a lot of machine code. The advantages are that you can use primitive
types, and you get code optimized for each type. (Java generics don’t work
with primitive types.)
pf2

Partial preview of the text

Download CS 61B: Lecture 15 - Exceptions, Generics, and Field Shadowing and more Study notes Data Structures and Algorithms in PDF only on Docsity!

CS 61B:

Lecture 15

Friday, October 1, 2010

Today’s

reading:

Sierra & Bates, pp. 189, 283.

EXCEPTIONS

(continued)

==========The

"finally"

keyword

---------------------A

finally

clause

can

also be added to a "try."

try

statementX;return

catch

(SomeException e) {

e.printStackTrace();return

finally

f.close();return

If

the

"try"

statement

begins to execute, the "finally" clause will be executed

at

the

end,

no

matter

what happens.

This example (which is contrived) always

returns

never

or

If

statementX

causes

a

SomeException, the exception is caught, the "catch"

clause

is

executed,

and

then the "finally" clause is executed.

If statementX

causes

some

other

class

of exception, the "finally" clause is executed

immediately,

then

the

exception continues to propagate down the stack.

"finally"

clauses

are

used to do things that need to be done in both normal and

exceptional

circumstances.

(Again, closing files is an example.)

In

the

example

above,

we’ve invoked the method "printStackTrace" on the

exception

we

caught.

When an exception is created, it takes a snapshot of the

stack,

which

can

be

printed later.

It

is

possible

for

an

exception to occur in a "catch" or "finally" clause.

An

exception

thrown

in

a

"catch" clause will terminate the "catch" clause, but the

"finally"

clause

will

still get executed before the exception goes on.

An

exception

thrown

in

a

"finally" clause replaces the old exception, and

terminates

the

"finally" clause and the method immediately.

However...you

can

put

a

"try" clause inside a "catch" or "finally" clause,

thereby

catching

those

exceptions as well.

Exception

constructors

----------------------By

convention,

most

Throwables (including Exceptions) have two constructors.

One

takes

no

parameters, and one takes an error message in the form of a

String.

class

MyException

extends Exception {

public

MyException() { super(); }

public

MyException(String s) { super(s); }

The

error

message

will

be printed if it propagates out of main(), and it can be

read

by

the

Throwable.getMessage() method.

The constructors usually call the

superclass

constructors, which are defined in Throwable.

GENERICS========Suppose you’re using a list

of

Objects

to

store

Strings.

When

you

fetch

a

String from the list, you have

to

cast

it

back

to

type

"String"

before

you

can

call the methods exclusive

to

Strings.

If

somehow

an

object

that’s

not

a

String got into your list,

the

cast

will

throw

an

exception.

It

would

be

nice

to have the compiler enforce

the

restriction

that

nothing

but

Strings

can

ever

get into your list in the first

place,

so

you

can

sleep

at

night

knowing

that

your family is safe from a

terrible

ClassCastException.

So Java offers generics,

which

allow

you

to

declare

general

classes

that

produce specialized objects.

For

example,

you

can

create

an

SList

for

Strings

only, and another SList for

Integers

only,

even

though

you

only

wrote

one

SList class.

To specify the

class,

SList

takes

a

type_parameter.

class SListNode {

T item;public SListNode next;SListNode(T i, SListNode

n)

item = i;next = n; } } public class SList {

SListNode head;public void insertFront(T

item)

head = new SListNode(item,

head);

} You can now create and use

an

SList

of

Strings

as

follows.

SList l = new SList();l.insertFront("Hello"); Likewise, you can create an

SList

of

Integers

by

using

"SList"

in

the

declaration and constructor.The advantage of generics here

is

that

the

compiler

will

ensure

at

compile-

time that nothing but Strings

can

ever

enter

your

SList,

so

there

will

be no nasty surprises at run

time.

Generics are a complicated

subject.

Consider

this

to

be

a

taste

of

them;

hardly a thorough treatment.

Sun

has

a

much

longer

tutorial

at

http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

Although Java generics are

superficially

similar

to

C++

templates,

there’s

a

crucial difference between

them.

In

the

example

above,

Java

compiles

bytecode

for only a single SList class.

This

SList

bytecode

can

be

used

by

all

different object types.

It

is

the

compiler,

not

the

bytecode

itself,

that

enforces the fact that a particular

SList

object

can

only

store

objects

of

a

particular class.

Conversely,

C++

recompiles

the

SList

methods

for

every

type

that you instantiate SLists

on.

The

disadvantage

is

that

one

class

might

turn

into a lot of machine code.

The

advantages

are

that

you

can

use

primitive

types, and you get code optimized

for

each

type.

(Java

generics

don’t

work

with primitive types.)

"for

each"

on

Arrays

====================Java

has

a

"for

each"

loop for iterating through the elements of an array.

int[]

array

for

(int

i

array)

System.out.print(i

Note

that

i

is

not

iterating from 0 to 5; it’s taking on the value of each

array

element

in

turn.

You can iterate over arrays of any type this way.

String

concat

for

(String

s

stringArray) {

concat

concat

s;

For

some

reason,

the

type declaration must be in the "for" statement.

The

compiler

barfs

if

you

try

int

i;

for

(i

array)

FIELD

SHADOWING

===============Just

as

methods

can

be

overridden in subclasses, fields can be "shadowed" in

subclasses.

However,

shadowing works quite differently from overriding.

Whereas

the

choice

of

methods is dictated by the dyanamic_type of an object,

the

choice

of

fields

is

dictated by the static_type of a variable or object.

class

Super

int

x

int

f()

return

} class

Sub

extends

Super {

int

x

// shadows Super.x

int

f()

return

// overrides Super.f()

Any

object

of

class

Sub

now has two fields called x, each of which store a

different

integer.

How

do we know which field is accessed when we refer to x?

It

depends

on

the

static type of the expression whose x field is accessed.

Sub

sub

new

Sub();

Super

supe

sub;

// supe and sub reference the same object.

int

i;

sub

|Sub.x Super.x |

supe

i

supe.x;

i

sub.x;

i

((Super)

sub).x;

i

((Sub)

supe).x;

The

last

four

statements all use the same object, but yield different results.

Recall

that

method

overriding does not work the same way.

Since both variables

reference

a

Sub,

the

method Sub.f always overrides Super.f.

i = supe.f();

i = sub.f();

i = ((Super) sub).f();

i = ((Sub) supe).f();

What if the variable whose

shadowed

field

you

want

to

access

is

"this"?

You can cast "this" too, but

a

simpler

alternative

is

to

replace

"this"

with

"super".

class Sub extends Super {

int x = 4;

shadows

Super.x

void g() {

int i;i = this.x;

i = ((Super) this).x

i = super.x;

Whereas method overriding is

a

powerful

benefit

of

object

orientation,

field

shadowing is largely a nuisance.

Whenever

possible,

avoid

having

fields

in

subclasses whose names are

the

same

as

fields

in

their

superclasses.

Static methods can be shadowed

too;

they

follow

the

same

shadowing

rules

as

fields.

This might seem confusing:

why

do

ordinary,

non-static

methods

use

one system (overriding) while

static

methods

use

an

entirely

different

system

(shadowing)?

The reason is

because

overriding

requires

dynamic

method

lookup.

A static method is not called

on

an

object,

so

there’s

nothing

whose

dynamic

type we can look up.

Therefore,

static

methods

can’t

use

dynamic

method

lookup or overriding.

So they

use

shadowing

instead.

Static method shadowing, like

field

shadowing,

is

largely

a

nuisance.

"final" METHODS AND CLASSES===========================A method can be declared "final"

to

prevent

subclasses

from

overriding

it.

Any

attempt to override it will

cause

a

compile-time

error.

A class can be declared "final"

to

prevent

it

from

being

extended.

Any

attempt

to declare a subclass will

cause

a

compile-time

error.

The only reason to declare

a

method

or

class

"final"

is

to

improve

the

speed

of

a program.

The compiler can

speed

up

method

calls

that

cannot

be

overridden.