Video

Friday, 1 July 2011

lesson 8

8. Inheritance and

Interfaces

To learn about

inheritances

To be able to convert

between supertype

and subtype

references

To understand how to

override superclass

methods

To understand the

concept of

polymorphism

To design and use

abstract classes and

interfaces

To learn about

packages and Java

access control

Introduction to

Inheritance

Inheritance is a

mechanism for

enhancing existing,

working class.

For example, suppose

we need to define a

class SavingsAccount

to model an account

that pays a fixed

interest rate on

deposits.

class SavingsAccount

extends BankAccount

{ new instance variables

new methods

}

class SubclassName

extends SuperclassName

{ new instance variables

new methods

}

Inheritance

The more general

class that forms the

basis for inheritance is

called the superclass.

The more specialized

class that inherits from

the superclass is

called the subclass.

One important reason

for inhertance is code

reuse.

Inheritance Diagram

Object <---
BankAccount <----
SavingsAccount
Inheritance Diagram
Which class is a
superclass and which
class is a subclass?
BankAccount is the
superclass and
SavingsAccount is the
subclass.
Suppose we want to
set an interest rate in
the constructor for
SavingsAccount class
and also a method to
apply that interest rate
periodically.
public class
SavingsAccount extends
BankAccount
{ private double
interestRate;
public SavingsAccont
(double rate)
{ constructor
implementation }
public void addInterest
( )
{ method
implementation }
}
public class
BankAccount
{ private double
balance;
public BankAccount( )
{ balance = 0; }
public BankAccount
(double init )
{ balance = init; }
public void deposit
(double amount)
{ balance = balance +
amount; }
public void withdraw
(double amount)
{ balance = balance -
amount; }
public double
getBalance( )
{ return balance; }
}
public class
SavingsAccount
extends BankAccount
{ private double
interestRate; //
instance variable
public SavingsAccont
(double rate) //
constructor
{ interestRate = rate; }
public void addInterest
( ) // instance method
{ double interest =
getBalance() *
interestRate / 100;
deposit(interest);
}
}
// Example Code
SavingsAccount
collegeFund = new
SavingsAccount(10);
collegeFund.deposit
(500); // OK to use
superclass’s method
collegeFund.
addInterest( );
Converting Between
Class Types
A SavingsAccount
object is a special
case of a BankAccount
object. So, you can
store a reference to a
SavingsAccount object
into a BankAccount
object.
SavingsAccount
collegeFund = new
SavingsAccount(10);
BankAccount anAccount =
collegeFund;
Object anObject =
collegeFund;
The three object
references stored in
collegeFund,
anAccount, and
anObject refer to the
same object of type
SavingsAccount.
However, the
anAccount knows less
than the full story
about the object to
which it refers
anAccount.deposit(1000)
; // OK
anAccount.addInterest( )
; // Not OK
Because anAccount is
an object of type
BankAccount, we
cannot use the
addInterest method.
anObject.deposit(1000); //
Not OK
The variable anObject
knows even less,
because it is an object
of type Object and
deposit is not a
method of the object
class.
Why would anyone
want to know less
about an object and
store a reference in an
object variable of a
superclass? Reuse.
Consider the transfer
method
void transfer(BankAccount
other, double amount)
{ withdraw(amount);
other.deposit(amount);
}
We can use this
method to transfer
money from one bank
account to another:
BankAccount
momsChecking = … ;
BankAccount
harrysChecking = … ;
momsChecking.transfer
(harrysChecking, 1000);
We can also use this
method to transfer
money into a
SavingAccount:
SavingsAccount
CollegeFund = … ;
momsChecking.
transfer(collegeFund,
1000);
However, we cannot
convert between
unrelated classes:
Rectangle r =
CollegeFund; // Error
Subclass reference can
be assigned to
superclass reference
Object anObject =
collegeFund;
But, superclass
reference must be
casted to subclass
reference.
SavingsAccount x =
(SavingsAcount)anObject;
as long as we
absolutely sure that
anObject really refers
to a SavingsAccount
object.
To play it safe, we can
use the instanceof
operator to test
whether an object
belongs to a particular
class (or one of its
subclasses).
Object instanceof
ClasssName
Rectangle r;
if (x instanceof
Rectangle)
r = (Rectangle) x;
SavingsAccount x;
if (anObject instanceof
SavingsAccount)
x = (SavingsAccount)
anObject;
else
x = null;
Hierarchies
Hierarchies are
frequently represented
as trees, with the
most general concepts
at the root.
In Java, it is common
to group classes in
inheritance
hierarchies.
Inheritance
Hierarchies
The classes
representing the most
general concepts are
near the root, more
specialized classes
towards the branches.
When designing a
hierarchy of classes,
those common
properties are
collected in a
superclass. More
specialized properties
can be found in
subclasses.
Consider a bank that
offers its customers
three kinds of
accounts:
CheckingAccount
SavingsAccount
TimeDepositAccount
The checking account
has no interest, gives
you a small number of
free transactions per
month, and charges a
transaction fee for
each additional
transactions.
The savings account
compounds interest
monthly.
The time deposit
account has a penalty
for early withdrawal.
The inheritance
hierarchy for these
account classes is:
protected Members
A superclass’s public
members are
accessible anywhere
the program has a
reference to that
superclass type or one
of its subclasses
types.
A superclass’s private
members are
accessible only in
methods of that
superclasses.
A superclass’s
protected members
serve as an
intermediate level of
protection between
public and private
access.
protected Members
A superclass’s
protected members
can be accessed by
methods of the
superclass, by
methods of subclasses
and by methods of
other classes in the
same package
(protected members
have package access).
Subclass methods can
normally refer to public
and protected
members of the
superclass simply by
using the member
names.
Inheritance
// Definition of a class
Point
public class Point {
protected int x, y;
public Point()
{ setPoint(0,0); }
public Point(int a, int b)
{ setPoint(a, b); }
public void setPoint(int
a, int b)
{ x = a;
y = b;
}
public int getX()
{ return x; }
public int getY()
{ return y; }
public String toString()
{ return “[“ + x + “, “ + y
+ “]”; } }
// Definition of class
circle
public class Circle
extends Point {
protected double
radius;
public Circle()
{ // implicit call to
superclass constructor
setRadius(0); }
public Circle(double r,
int a, int b)
{ super(a, b);
setRadius(r);
}
public void setRadius
(double r)
{ radius = (r >= 0.0 ? r :

0.0); }

pubic double getRadius

()

{ return radius; }

public double area()

{ return Math.PI *

radius * radius; }

public String toString()

{ return “Center = “ +

“[“ + x + “,” + y + “]” +

“; Radius = “ + radius;

}

}

// Main program

import java.text.

DecimalFormat;

import javax.swing.

JOptionPane;

public class

InheritanceTest {

public static void main

(String[] args)

{ Point pointRef, p;

Circle circleRef, c;

String output;

p = new Point(30,50);

c = new Circle(2.7, 120,

89);

output = “Point p: “+

p.toString() +

“\nCircle c: “ +

c.toString() ;

pointRef = c;

output += “\n\nCircle c

(via pointRef): “ +

pointRef.toString() ;

circleRef = (Circle)

pointRef;

output += “\n\nCircle c

(via circleRef): “ +

circleRef.toString() ;

DecimalFormat

precision2 = new

DecimalFormat(“0.00”)

;

output += “\n\nArea of

c (via circleRef): “ +

precision2.format

(circleRef.area());

if (p instanceof Circle)

{

circleRef = (Circle) p;

output += “\n\ncast

successful”;

}

else

output += “\n\n p does

not refer to a circle”;

JOptionPane.

showMessageDialog

(null, output,

“Inheritance”,

JOptionPane.

INFORMATION_

MESSAGE);

System.exit(0);

}

}

Inheriting Instance

Variables and Methods

When defining

additional methods for

a subclass, there are 3

options:

1. We can override

methods from the

superclass by

specifying a method

with the same

signature (i.e., the

same name and the

same parameters).

2. We can inherit

methods from the

superclass. If we do

not explicitly override

a superclass method,

we automatically

inherit it.

3. We can define new

methods that does not

exist in the superclass.

When defining

additional instance

variables for a

subclass, there are 2

options:

1. We can inherit

variables from the

superclass.

2. We can define new

variables. These new

variables are only

presented in subclass

objects.

Remember! We can

never override

instance variables.

Shadowing

The newly defined

subclass variable

shadows the

superclass variable.

The superclass

variable is still

present, but it cannot

be accessed.

When you refer to

balance in a

SavingAccount

method, you access

the new instance

variable.

Shadowing instance

variables is harmless.

Shadowing Instance

Variables

A subclass has no

access to the private

instance of the

superclass

public class

CheckingAccount

extends BankAccount

{ public void deposit

(double amount)

{ transactionCount++;

balance = balance +

amount; // ERROR

}

}

To solve the problem

public class

CheckingAccount

extends BankAccount

{ private double

balance; // shadow

variable

public void deposit

(double amount)

{ transactionCount++;

balance = balance +

amount; // OK but

different balance

}

}

Example

public class

BankAccount

{ private double

balance;

public double

getBalance() {…}

public void deposit

(double amount) {…}

public void withdraw

(double amount) {…}

}

public class

CheckingAccount

extends BankAccount

{ private int

transactionCount;

public void deposit

(double amount) {…}

public void withdraw

(double amount) {…}

public void deductFees

() {…}

}

Subclass methods

have no access rights

to the private data of

the superclass.

If we want to modify a

private superclass

variable, we must use

a public method of the

superclass.

Can we write the

deposit method as

public void deposit

(double amount)

{ transactionCount++;

deposit(amount); // Is

it OK?

}

If we define the

method like that, it will

call itself infinitely.

We need to be more

specific that we want

to invoke the

superclass’s deposit

method by using a

special keyword super

for this purpose:

public void deposit

(double amount)

{ transactionCount++;

super.deposit(amount);

}

Calling a Superclass

Methods

public class

CheckingAccount

extends BankAccount

{ private static final int

FREE_TRANSANCTIONS

= 3;

private static final

double TRANSACTION_

FEE = 2.0;

public void withdraw

(double amount) //

override method

{ transactionCount++;

super.withdraw

(amount); // calling a

superclass method.

// what if we call

withdraw(amount);

}

public void deductFees

( )

{ if (transactionCount >

FREE_TRANSACTIONS)

{ double fees =

TRANSACTION_FEE

* (transactionCount -

FREE_TRANSACTIONS);

super.withdraw(fees);

}

}

Now, let consider the

TimeDepositAccount

class, a subclass of the

SavingsAccount:

public class

TimeDepositAccount

extends

SavingsAccount

{ private int

periodsToMaturity;

private static final

double EARLY_

PENALTY= 20.0;

public void addInterest

( )

{ periodsToMaturity- - ;

super.addInterest( );

}

public void withdraw

(double amount)

{ if (periodsToMaturity

> 0)

super.withdraw(EARLY_

PENALTY);

super.withdraw

(amount);

}

}

The

TimeDepositAccount

is two levels away

from the BankAccount.

The BankAccount is a

superclass, but not the

immediate superclass.

The

TimeDepositAccount

inherits two methods

from the BankAccount

class, namely

getBalance, and

deposit.

Methods can be

inherited from an

indirect superclass as

long as none of the

intermediate

superclasses

overrides them.

The method call

super.addInterest in

addInterest method of

the

TimeDepositAccount

refers to the

addInterest method in

SavingsAccount class.

The super.withdraw in

the withdraw method

of the

TimeDepositAccount

refers to the withdraw

method in

BankAccount.

Subclass Construction

Calling a Superclass

Constructor

ClassName

(parameters)

{ super(parameters);



}

Call the superclass

constructor to set the

balance to the initial

balance

public class

CheckingAccount

extends BankAccount

{ public

CheckingAccount

(double init)

{ super(init);

transactionCount = 0;

}

}

The constructor call

must be the first

statement of the

subclass constructor.

Subclass Constructor

When the keyword

super is followed by a

parenthesis, it

indicates a call to the

superclass

constructor.

If super is followed by

a period and a method

name, it indicates a

call to a superclass

method.

Or, we can implement

the CheckingAccount

constructor without

calling the superclass

constructor as can be

seen in the following

example:

public class

CheckingAccount

extends BankAccount

{ public

CheckingAccount

(double init)

{ // use superclass’s

default constructor

super.deposit(init);

transactionCount = 0;

}

}

public class

TimeDepositAccount

extends

SavingsAccount

{ public

TimeDepositAccount

(double rate, int

maturity )

{ super(rate);

periodsToMaturity =

maturity ;

}

}

Subclass Construction

If a subclass

constructor does not

call the superclass

constructor, the

superclass is

constructed with its

default constructor.

If a superclass does

not have a default

constructor, then the

complier reports an

error.

For example,

CheckingAccount can

be implemented

without calling the

superclass

constructor.

The BankAccount class

is constructed with its

default constructor,

which set the balance

to zero.

Then the

CheckingAccount

constructor must

explicitly deposit the

initial balance.

public

CheckingAccount

(double init)

{ // use superclass’s

default constructor

super.deposit(init);

transactionCount = 0;

However, in the case

of

TimeDepositAccount,

the superclass,

SavingsAccount, has

no default constructor.

Thus, we must call the

superclass constructor

explicitly.

public

TimeDepositAccount

(double rate, int

maturity )

{ super(rate);

periodsToMaturity =

maturity ;

}

Polymorphism

“is-a” relationship:

Every object of the

subclass is also a

superclass object, but

with special

properties.

For example, every

CheckingAccount is a

BankAccount.

It is possible to use a

subclass object in

place of a superclass

object.

What is polymorphism?

Let’s consider the

transfer method that

can be used to transfer

money from another

account:

public void transfer

(BankAccount other,

double amount)

{ withdraw(amount);

other.deposit(amount);

}

BankAccount

collegeFund = … ;

CheckingAccount

harrysChecking = … ;

collegeFund.transfer

(harrysChecking, 1000)

;

Which deposit

method?

BankAccount.deposit( )

or CheckingAccount.

deposit( )

Consider the call to the

deposit method, the

other parameter has

type BankAccount.

On the other hand, the

CheckingAccount class

provides its own

deposit method that

updates the

transaction count.

Since the other

variable actually refers

to an object of the

subclass

CheckingAccount, it

would be appropriate if

the CheckingAccount.

deposit method was

called instead.

The principle that the

actual type of the

object determines the

method to be called is

called polymorphism.

In Java, method calls

are always determined

by the type of the

actual object, not the

type of the object

reference.

In Java, all instance

methods are

polymophic.

The term

“polymorphism”

comes from the Greek

words for “many

shapes”.

Program

AccountTest.java

public class AccountTest

{ public static void main

(String[] args)

{ SavingsAccount

momsSavings = new

SavingsAccount(0.5);

TimeDepositAccount

collegeFund =

new TimeDepositAccount

(1, 3);

CheckingAccount

harrysChekcing = new

CheckingAccount(0);

momsSavings.deposit

(10000);

collegeFund.deposit

(10000);

momsSavings.transfer

(harryschecking, 2000);

collegeFund.transfer

(harryschecking, 980);

harrysChecking.withdraw

(500);

harrysChecking.withdraw

(80);

harrysChecking.withdraw

(400);

endOfMonth

(momsSavings);

endOfMonth

(collegeFund);

endOfMonth

(harrysChecking);

printBalance(“mom’s

savings”,

momsSavings);

printBalance(“the

college Fund”,

collegeFund);

printBalance(“Harry’s

checking”,

harrysChecking);

}

public static void

endOfMonth

(SavingsAccount savings)

{ savings.addInterest

( ); }

public static void

endOfMonth

(CheckingAccount

checking)

{ checking.deductFees

( ); }

public static void

printBalance(String

name, BankAccount

account)

{ System.out.println

(“The balance of “ +

name + “ account is $”

+

account.getBalance( ) )

;

}

}

Polymorphism vs.

Overload

Let’s take a look at a

polymorphic method

call such as

other.deposit(amount),

there are several

deposit methods that

can be called.

Both polymorphic

deposit and withdraw

methods are called.

What happens when

the method name is

overloaded;i.e., when

a single class has

several methods with

the same name but

different parameter

types?

For example, we can

have two constructors

BankAccount() and

BankAccount(double).

The compiler selects

the appropriate

method when

compiling a program.

Another example of

overloading, the static

endOfMonth methods

in the AccountTest

class. The compiler

uses the type of the

explicit parameter to

pick the appropriate

method.

The main difference

between

polymorphism and

overloading is the

binding method.

Polymorphism uses a

selection method

called late binding, i.e.,

the compiler doesn’t

make any decision

when translating the

method, it is the virtual

machine that selects

the appropriate

method.

Overloading uses early

binding technique

which compiler picks

an overloaded method

when translating the

program.

Interfaces

Many object-oriented

programming

languages have

multiple inheritance,

i.e.,more than one

superclass.

In Java, a class cannot

have two direct

superclasses.

To support this

feature, we use

interfaces instead of

superclasses.

The purpose of

interfaces is to support

the concept of writing

a single method for

any data type.

Interfaces

An interface is similar

to a class, but

An interface does not

have instance

variables

All methods in an

interface are abstract;

they have a name,

parameters, and a

return type, but they

don’t have an

implementation.

All methods in an

interface are

automatically public.

The interface

declaration lists all

methods that the

interface requires.

For example, the

java.lang package

defines a Comparable

interface as:

public interface

Comparable

{ int compareTo(Object

other);

// no implementation

}

We don’t extend

interfaces; we

implement them, using

the special

implements keyword:

public class

SavingAccount extends

BankAccount

implements

Comparable

{ …

}

Any class that

implements the

Comparable interface

must supply the

compareTo method

public class

SavingsAccount

extends BankAccount

implements

Comparable

{ …

public int compareTo

(Object other)

{ // supply

implementation …

SavingsAccount

otherAcc =

(SavingsAccount)

other;

if (interestRate <
otherAcc.interestRate)
return -1;
if (interestRate >

otherAcc.interestRate)

return 1;

return 0;

} ...

}

Note that the class

must declare the

method as public,

whereas the interface

does not -- all methods

in an interface are

public.

Once a class

implements an

interface, you can

convert a class

reference to an

interface reference:

SavingsAccount

harrysSavings = new

SavingAccount();

Comparable first =

harrysSavings;

However, we can

never construct an

interface:

Comparable second; // OK

second = new

Comparable( ); // ERROR

All interface

references refer to

objects of other

classes-- classes that

implement the

interface.

first.compareTo(second);

A class can have only

one superclass, but it

can implement any

number of interfaces

public class

SavingsAccount

extends BankAccount

implements

Comparable, Cloneable

Constants in

Interfaces

public interface

SwingConstants

{ int NORTH = 1;

int NORTH_EAST = 2;

int EAST = 3;



}

// SwingConstants.

NORTH;

all variables in an

interface are

automatically public

static final

Abstract Classes

Abstract Classes : A

class which we cannot

create objects

public abstract class

BankAccount

{ publicabstract void

deductFees( );



}

The reason for using

abstract classes is to

force programmers to

create subclasses.

BankAccount

anAccount; // OK

anAccount = new

BankAccount(); // Error

anAccount = new

SavingsAccount(); //

OK

anAccount = null; // OK

Note that we can still

have an object

reference whose type

is an abstract class.

Abstarct classes differ

from interfaces -- they

can have instance

variables and concrete

methods.

Final Methods and

Classes: To prevent

others from creating

subclasses or from

overriding certain

methods

public final class String

{ …. }

public class MyApplet

extends Applet

{ publicfinal boolean

checkPasswd(String

passwd)

{ … }

}

Access Control

Java has four levels of

controlling access to

variables, methods,

and classes:

public access

private access

protected access

Package access (the

default, when no

access modifier is

given)

Proteced data can be

accessed by the

methods of a class and

all its subclasses.

public class

BankAccount

{ protected double

balance; }

If a superclass

declares a method to

be publicly accesible,

we cannot override it

to be more private.

public class BankAccount

{ public void withdraw

(double amount) { … } }

public class

TimeDepositAccount

extends BankAccount

{ private void withdraw

(double amount) { … } //

ERROR

}

Overriding Object

Superclass Methods

The methods of the

Object class are very

general:

- String toString( ) //

Returns a string

representation of the

object

- boolean equals

(Object other) // Test

whether the object

equals

// another object

- Object clone( ) //

Makes a full copy of an

object

Overriding the toString

Method

The toString method

returns a string

representation for

each object.

Rectangle x = new

Rectangle(5, 10, 20, 30);

String s = x.toString( ); //

set s to

“java.awt.Rectangle[x=5,

// y=10,

width=20,height=30]”

The toString method is

called whenever we

concatenate a string

with an object.

System.out.println(“x

means ” + x); // will print

“x means

// java.awt.Rectangle

[x=5,y=10,width=20,height

=30]”

Let’s try the toString

method for the

BankAccount class:

BankAccount x = new

BankAccount(5000);

String s = x.toString( ); //

sets s to “BankAccount@

d246bf”

// print the name of the

class and the memory

address of the object

To work better,

overriding toString

method:

public class BankAccount

{ public String toString()

{ return “BankAccount

[balance=” + balance + “]

”; }

}

BankAccount x = new

BankAccount(5000);

String s = x.toString( );

// sets s to

“BankAccount

[balance=5000]”

Overriding the equals

Method

The equals method is

called whenever we

want to compare two

objects:

if (account1.equals

(account2)) …

// contents are the same

Compare to:

if (account1 = = account2)



// test two references are

the same object

public class

BankAccount

{ public boolean equals

(Object otherObj)

{ if (otherObj

instanceof

BankAccount)

{ BankAccount other =

(BankAccount)

otherObj;

return balance = =

other.balance;

}

else

return false;

}

}

Overriding the clone

method

To make a copy of an

object:

public class BankAccount

{ public Object clone( )

{ BankAccount clonedAcc

= new BankAccount

(balance);

return clonedAcc;

}

}

When we call the

method:

BankAccount acc1 = new

BankAccount(1000);

BankAccount acc2 =

(BankAccount)acc1.clone();

Compared to:

BankAccount acc1 =

new BankAccount

(1000);

BankAccount acc2 =

acc1;

Clone Mutable

Instance Variables

Consider the following

class:

public class Customer

{ private String name;

private BankAccount

account;

public Customer(String

aName)

{ name = aName;

account = new

BankAccount();

}

public String getName()

{ return name; }

public BankAccount

getAccount()

{ return account; }

getAccount method

breaks encapsulation

because anyone can

modify the object

state without going

through the public

interface:

Customer harry = new

Customer(“Harry

Handsome”);

BankAccount account =

harry.getAccount();

// anyone can

withdraw money!

account.withdraw

(100000);

So, we should clone

the object reference:

public BankAccount

getAccount()

{ return (BankAccount)

account.clone(); }

The rule of thumb is

that a class should

clone all references to

mutable objects that it

gives out.

The converse is true

as well-- a class

should clone

references that it

receives:

public Customer(String

aName, BankAccount

anAccount)

{ name = aName;

account = (BankAccount)

anAccount.clone();

}

Using Object.clone()

If we have an object

with many instance

variables, it can be

tedious to copy them

all into a new object.

The Object.clone()

simply creates a new

object whose instance

variables are copies of

the original.

public class

BankAccount

{ public Object clone()

{ // not complete

Object clonedAccount

= super.clone();

return clonedAccount;

}

}

Object.clone() checks

that the object being

cloned implements the

Cloneable interface

and do exception

handling.

public class BankAccount

implements Cloneable

{ public Object clone()

{try

{ // clones all instance

variables

Object clonedAccount =

super.clone();

return clonedAccount;

}

catch (

CloneNotSupportedEx

ception e)

{ return null; }

}

}

If an object contains a

reference to another

mutable object, then

we need to call clone

for that reference, in

order to prevent

shallow copy problem

(see Figure 11).

public class Customer

implements Cloneable

{ private String name;

private BankAcount

account;

public Object clone()

{ try

{ Object

clonedCustomer =

(Customer)super.clone

();

clonedCustomer.

account =

(BankAccount)

account.clone();

return

clonedCustomer;

}

catch (

CloneNotSupportedEx

ception e)

{ return null; }

}

}

Packages

A Java package is a

set of related classes.

Table1: Important

Packages in the Java

Library

java.lang Langauge

supoort Math

java.util Utilites

Random

java.io Input/Output

PrintStream

java.awt Abstract

Windowing Color

Toolkit

java.applet Applets

Applet

java.net Networking

Socket

java.sql Database

access ResultSet

javax.swing Swing UI

JButton

To put classes in a

package:

package

packagename;

package

com.horstmann.ccj;

import

java.io.InputStream;

import java.io.

InputStreamReader;

public class

ConsoleReader { … }

….

Importing Packages

import

com.horstmann.ccj.

ConsoleReader;

0 comments:

Post a Comment

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Sweet Tomatoes Printable Coupons