Due:
Sunday evening, November 14 by midnight via turnin.
If you have problems with turnin
from outside the Elements Lab,
be sure to use turnin
from the Elements Lab
so that you submit your work on time.
New Requirement:
Starting with this assignment,
your Java class definitions
should all be placed
into package "edu.utexas.cs303e.student"
or,
when explicitly stated,
a
sub-package
thereof.
If an exercise does not explicitly state the package,
you should place it into edu.utexas.cs303e.student.
For example,
if an exercise asks you
to place your implementation
into a "class named AnArbitraryName",
your source code should define a class
which has
full name
edu.utexas.cs303e.student.AnArbitraryName.
Purpose: This assignment gives you practice with inheritance and packages. In particular, you will be asked to define collaborations of multiple classes in certain exercises. A collaboration is a group of classes that are expected to work together.
Remember: If you aren't clear on a topic covered in this class, you can post on the class newsgroup or you can attend the many office hours held by the teaching staff. In particular, if you're not clear on any of the terms or concepts described in this assignment, please ask questions. The earlier you ask your questions, the better!
Part 1: Inheritance Basics. In this part, your overall task is to implement an inheritance hierarchy to represent words that are cognate to English words. Even though there are stricter definitions of "cognate", we'll simply define it as follows: A word in one language is a cognate of a word in another language if they have the same meaning. For example, the French word "pied" is a cognate of the English word "foot".
(20 points)
Start by implementing an
immutable (no mutators!)
class
named Word
that contains an attribute
representing a word that you might use in a sentence.
Be sure to define an appropriate instance variable
for this attribute.
(What package should you use to define this class?)
Class Word must
have one public constructor
and two public methods as defined below:
public Word (String word)
which builds an instance of
a non-null, non-empty
word.
Remember to error-check
the parameter.
In this case,
a valid word should certainly contain no whitespace.
You might also be able
to think of some other error-checking to do.
public String getWord()
which returns the word attribute.
public String toString()
which does exactly the same thing as method getWord().
In addition to the public constructor and methods
defined above,
you may also define as many protected
methods as you want.
Such methods are not necessary in this exercise,
but well-chosen protected methods
might help you to implement
some of the other exercises
in this assignment.
Put your class definition into a file named Word.java
and submit it via turnin.
(45 points)
Next,
implement an
immutable (no mutators!)
class named CognatePair
(in which package?)
that extends Word.
Instances of this class will define a pair of words,
one in English
and the other in another language.
The two words will be cognates.
Your implementation should have
exactly one
constructor:
protected CognatePair (String word, String cognate)
which definitely
should have access mode protected,
not public,
private,
or package.
Note:
When a class has only protected
constructors,
as this one does,
it is not possible
to create instances
except from subclasses
or from the class itself
(via a factory method or as an embedded instance).
This constructor should error-check
its parameters
to ensure that both are non-null,
non-empty words
as defined in the previous exercise.
The first word
(parameter "word")
should be supplied (using keyword super)
to the constructor
available in the superclass Word.
This will initialize the word attribute.
Then,
the parameter "cognate" should be used
to initialize an appropriate variable
in this instance.
As a result,
your CognatePair class
will have two attributes:
The word defined in the superclass,
and a new cognate attribute
representing the cognate word
defined in this class.
Based on this information so far,
it should seem reasonable
to you that your CognatePair class will
have an accessor method
for each word:
public String getWord()
which will be inherited from Word.
This means that you don't have to write
this method again in class CognatePair!
public String getCognate(),
however,
must be implemented in class Cognate
since it is a new attribute
that doesn't exist in the superclass.
Method getCognate()
must return the cognate initialized in the constructor.
However,
there will also be
another
accessor,
public String getCognateLanguage().
This will return
the language of the cognate word
in each instance.
In your source code for the CognatePair class,
implement this method as follows:
public String getCognateLanguage() {
throw new IllegalStateException ("not implemented") ;
}
This accessor will be overridden
with a new definition
in each subclass
of CognatePair.
The overriding definition
will return an appropriate language
(more on that in later exercises).
Finally,
you should provide a
public String toString() implementation
that returns a String formatted as follows:
<language> word "<cognate>" means "<word>" in English
where <language> is replaced with the language
of the cognate,
where <cognate> is replaced with the cognate itself,
and
where <word> is replaced with the English word.
For example,
if the language of the cognate is French,
the English word is "fish",
and the cognate is "poisson",
then toString()
should return:
French word "poisson" means "fish" in English
It may seem hard to test class CognatePair
since you can't call the constructor
from BlueJ.
However,
for debugging purposes only,
you may implement a standard main method
in CognatePair
to create and test instances of CognatePair.
This is possible
because the main
method will be in the same class
as the protected constructor and,
therefore,
will be able to access the constructor.
Put your class definition into a file named
CognatePair.java and submit it via turnin.
(15 points)
Ok,
now it's time
to actually make use
of the above classes.
For one use,
define a class named FrenchCognate
in package edu.utexas.cs303e.student.french.
This class
extends
your class CognatePair
defined above
(remember to import CognatePair).
The subclass FrenchCognate
will define one constructor and one method
as described below:
public FrenchCognate (String english, String french)
simply passes its two parameters
to the super constructor
provided by class CognatePair.
Assuming you've correctly implemented
CognatePair and Word,
there is no additional code required
in the constructor.
Why?
public String getCognateLanguage()
overrides
the definition in class CognatePair.
The implementation in FrenchCognate
should simply return the String literal "French".
At this point,
you should be able to test class FrenchCognate
in BlueJ
using FrenchCognate's constructor.
This will further test classes
CognatePair and Word.
Why?
Put your class definition into a file named
FrenchCognate.java and submit it via turnin.
(25 points)
As another use of class CognatePair
define a class named PigLatinCognate
in package edu.utexas.cs303e.student.pig_latin.
This class again
extends
your class CognatePair
defined above
(remember to import CognatePair).
It will define one public constructor,
one public method,
and one private method
as described below:
private method,
which will also be static.
Method
private static String toPigLatin (String word)
translates the word parameter
to its Pig Latin cognate.
This translation has three parts:
(1) If word begins with a vowel,
then the Pig Latin cognate is defined
by appending "ay"
to word;
(2) If word contains
no vowels at all,
the Pig Latin cognate is again defined
by appending "ay"
to word;
and
(3) If word begins with consonants
followed by a vowel,
then the starting consonants are stripped from word
and appended to the remainder
followed by "ay".
As examples,
here is a table
showing one English word
of each type along with their Pig Latin cognates:
| English | Pig Latin |
|---|---|
| animal | animalay |
| grrr | grrray |
| program | ogrampray |
public PigLatinCognate (String english)
has only one parameter,
but it must also invoke
the super constructor
provided by class CognatePair,
which takes two parameters.
The first parameter will be an English word
as specified in parameter english,
and the second parameter will be the Pig Latin cognate
computed by invoking toPigLatin(english).
Again,
assuming you've correctly implemented
CognatePair and Word,
there is no additional code required
in the constructor.
Think about this for a bit!
The superclass' constructor requires two parameters and,
to correctly define a Pig Latin cognate,
it must be computed from the
English word supplied as the
single
parameter to the subclass constructor.
However,
we can't invoke
any instance methods
because the instance has not yet been constructed
-
no constructor has finished its work!
A static method is used
because static methods
don't depend on instance data.
public String getCognateLanguage()
overrides
the definition in class CognatePair.
The implementation in PigLatinCognate
should simply return the String literal "PigLatin".
At this point,
you should be able to test class PigLatinCognate
in BlueJ
using PigLatinCognate's constructor.
Put your class definition into a file named
PigLatinCognate.java and submit it via turnin.
Part 2: Word Collaborations.
Words are typically combined
into phrases and sentences,
so it should seem reasonable
that we would want to define
classes that combine multiple
Word instances
as defined in Part 1.
In this part,
you're asked to define a class Phrase
that combines Word instances.
In the implementation,
you'll see another use of recursion.
The simplest kind of phrase contains only one word.
For example, the phrase "Arghh" might indicate frustration.
On the other hand, phrases with multiple words can be viewed as
a combination of a leading word and a suffix
phrase.
For example, the phrase "Eighty percent of success is showing
up" (attributed to Woody Allen) can be viewed as the combination
of the leading word "Eighty" and the suffix phrase "percent of
success is showing up".
This is where recursion shows up:
We're defining a multi-word phrase
to be a combination of a word
and a suffix phrase,
which may again be a multi-word phrase.
So the definition of "phrase"
recursively depends
on itself.
We'll make use of this type of recursion
in the definition of class Phrase below.
(45 points)
Define an immutable class Phrase
(in which package?)
with the following constructors and methods:
public Phrase (Word word, Phrase suffix)
defines a multi-word phrase,
where word is the leading word
and suffix is the suffix phrase.
As usual,
error-check
the parameters
word
and
suffix
by ensuring that they are non-null.
However,
if classes Word and Phrase
are defined correctly,
no other error-checking needs
to be done.
Why?
After error-checking,
place the two parameters
into appropriate instance variables,
one for the leading word
and the other for the suffix phrase.
public Phrase (Word word)
defines a single-word phrase.
Error-check the parameter word
to ensure that it's non-null.
Then,
place it into an appropriate instance variable.
Note:
In this constructor,
there is no suffix phrase,
so define the instance variable representing
the suffix
to be null.
This should be the only
way that the suffix can be null.
public Phrase getSuffix()
returns the suffix phrase,
which may be null
when the phrase is a single-word phrase.
public Word getWord()
returns the leading word,
which will never
be null
since no constructor
ever allows it to be null.
public String toString()
returns the entire phrase
as a String.
If the suffix phrase is null,
it just returns the leading word
after converting it to a String
using the word's getWord() method.
Otherwise,
it returns the leading word
(converted to a String with getWord()),
followed by a space,
followed by the suffix phrase
(also converted to a String using its toString()).
When testing your implementation,
be sure to test both single-word
and multi-word phrases.
For example,
the following code should define several multi-word
phrases,
and the last one,
in variable eighty,
should be the entire "Eighty percent..." quote from Woody Allen.
The code shown should print
the quote as it is built word-by-word.
The last line printed
should show the entire quote.
If you use this code
as part of your testing,
be sure to import
the referenced classes.
Phrase up = new Phrase (new PigLatinCognate ("up")) ;
System.out.println (up) ;
Phrase showing = new Phrase (new PigLatinCognate ("showing"), up) ;
System.out.println (showing) ;
Phrase is = new Phrase (new PigLatinCognate ("is"), showing) ;
System.out.println (is) ;
Phrase success = new Phrase (new PigLatinCognate ("success"), is) ;
System.out.println (success) ;
Phrase of = new Phrase (new PigLatinCognate ("of"), success) ;
System.out.println (of) ;
Phrase percent = new Phrase (new PigLatinCognate ("percent"), of) ;
System.out.println (percent) ;
Phrase eighty = new Phrase (new PigLatinCognate ("Eighty"), percent) ;
System.out.println (eighty) ;
Note:
It's perfectly legal
to use a PigLatinCognate
as a Word argument
in the constructors to Phrase,
as shown above.
Why?
Put your implementation of Phrase
into a file named Phrase.java
and submit it via turnin.
That's all you have to do for this assignment.
However,
you should probably also think about the following problem:
What should you do if you want to implement
a CognatePhrase class
which has a toString() method
that prints each instance in its cognate language?
We'll revisit this problem in the future.