Download Lecture 7

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Lecture 7
CS 202
clone() and Cloneable
• Cloneable is an interface that marks implementing classes as ones that can
be "cloned", ie copied. The method that does this is normally called clone().
However, the interface does not actually contain the clone() method, so there
is no guarantee that a call to clone() will always work on every Cloneable.
• clone() methods return deep copies of objects, so a variable that refers to a
clone points to a separate object, not the one that was cloned
• Object contains a protected clone() method which you need to call from your
own clone methods, but Object.clone() will not copy objects to which your
instance variables point.
– If these are mutable, your clone() method must also copy them. If you fail to do
this, your variables will point to the same objects the original object variables
pointed to. This can lead to hard-to-diagnose bugs.
• Cloneable is not widely used because of its design flaws. After the following
example, I will discuss other ways to accomplish deep copying.
2
clone() and Cloneable
package monsters;
public interface Monster {
public void setName(String name);
public String getName();
public void setLocation(String location);
public void rampage();
public String getOriginStory();
}
3
clone() and Cloneable
package monsters;
public class Crypt implements Cloneable {
private String location;
public Crypt(String location) {
this.location = location;
}
public void setLocation(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
public String toString(){
return "a mysterious crypt in " + location;
}
@Override
public Crypt clone(){
return new Crypt(location);
}
}
4
clone() and Cloneable
package monsters;
public class Vampire implements Monster, Cloneable {
private String name;
private Crypt crypt;
public Vampire(String nameIn, String location) {
name = nameIn;
crypt = new Crypt(location);
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setLocation(String location) {
crypt.setLocation(location);
}
@Override
public String getOriginStory() {
return "undead creature which lives by sucking the blood of living humans";
}
5
clone() and Cloneable
@Override
public void rampage() {
System.out.println(name + " arises from "
+ crypt.toString() + " as a bat, flies around and sucks people's blood all night, then returns to his coffin"
+ " to hide from sunlight");
}
@Override
public Vampire clone() {
Vampire newV;
try {
/*
* Object clone() returns an Object. To get to anything specific to Vampires, we need to cast it
* to a Vampire and use a Vampire reference variable
*/
newV = (Vampire) super.clone();
newV.crypt= crypt.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
return newV;
}
}
6
Clone and Cloneable
package monsters;
public class MonsterAttackDriver {
public static void main(String[] args) {
Vampire v1 = new Vampire("Dracula", "Transylvania");
// clone() returns an Object, but it will be a Vampire. To use methods that are not inherited from Object, we
need to cast.
Vampire v2 = (Vampire) v1.clone();
v2.setName("Bob");
v2.setLocation("Burbank");
v1.rampage();
v2.rampage();
}
}
7
Copy Constructor
• An alternative approach is to write a copy constructor for each class you
need to be able to "clone."
• Write a constructor that takes an instance of the class as an argument and
deep copies the data to create an identical instance.
• If data includes reference types, need to be careful to deep-copy it!
• Some programmers would write a method called newInstance() that calls
a constructor and setters to accomplish the same thing
package monsters;
Copy Constructors
public class RobotSoldier implements Monster{
private String location;
private SerialNumber serialNumber;
private String name;
public RobotSoldier(String location, SerialNumber serialNumber){
this.name = "RobotSoldier " + serialNumber;
this.location = location;
this.serialNumber = serialNumber;
}
public RobotSoldier(RobotSoldier r){
this.location = r.location;
// bad code! easy trap with reference types!
this.serialNumber = r.serialNumber;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName(){
return name;
}
Copy Constructors
@Override
public void setLocation(String location) {
this.location = location;
}
public SerialNumber getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(SerialNumber serialNumber) {
this.serialNumber = serialNumber;
}
public void changeSerialNumber(String prefix, int number){
serialNumber.setPrefix(prefix);
serialNumber.setNumber(number);
}
@Override
public void rampage() {
System.out.println("Robot Soldier " + serialNumber + " joins an army of robot soldiers to seize power in " +
location+ " as part of a plot to take over the world");
}
@Override
public String getOriginStory() {
return "evil robot soldier created by a mad scientist";
}
}
Copy Constructors
package monsters;
public class SerialNumber {
private String prefix;
private int number;
public SerialNumber(String inString, int inInt){
prefix = inString;
number = inInt;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String toString(){
return prefix + "-" + number;
}
}
Copy Constructors
package monsters;
import java.util.ArrayList;
import java.util.List;
public class MadScientist {
private String name;
List<RobotSoldier> army = new ArrayList<RobotSoldier>();
public MadScientist(String name) {
this.name = name;
}
public void createArmy(RobotSoldier r, int armySize) {
for (int counter = 0; counter < armySize; counter++) {
RobotSoldier n = new RobotSoldier(r);
n.changeSerialNumber("Chi", counter);
army.add(n);
}
}
public void addSoldier(RobotSoldier r) {
army.add(r);
}
public void mountPutsch() {
for (RobotSoldier r : army)
r.rampage();
}
}
Copy Constructors
package monsters;
public class MonsterAttackDriver {
public static void main(String[] args) {
RobotSoldier r = new RobotSoldier("Chicago", new SerialNumber("CHI", 1));
MadScientist m = new MadScientist("Dr. Evil");
m.createArmy(r, 5);
m.mountPutsch();
r.changeSerialNumber("CHI", 2);
System.out.println(r.getSerialNumber());
m.mountPutsch();
}
}
Corrected Copy Constructor
package monsters;
public class SerialNumber {
private String prefix;
private int number;
public SerialNumber(String inString, int inInt){
prefix = inString;
number = inInt;
}
// give SerialNumber a copy constructor too!
public SerialNumber(SerialNumber s){
this.prefix = s.prefix;
this.number = s.number;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String toString(){
return prefix + "-" + number;
}
}
Corrected Copy Constructor
package monsters;
public class RobotSoldier implements Monster{
private String location;
private SerialNumber serialNumber;
private String name;
public RobotSoldier(String location, SerialNumber serialNumber){
this.name = "RobotSoldier " + serialNumber;
this.location = location;
this.serialNumber = serialNumber;
}
public RobotSoldier(RobotSoldier r){
this.location = r.location;
// make a deep copy of serialNumber using its copy constructor!
this.serialNumber = new SerialNumber(r.serialNumber);
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName(){
return name;
}
@Override
public void setLocation(String location) {
this.location = location;
}
Corrected Copy Constructor
public SerialNumber getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(SerialNumber serialNumber) {
this.serialNumber = serialNumber;
}
public void changeSerialNumber(String prefix, int number){
serialNumber.setPrefix(prefix);
serialNumber.setNumber(number);
}
@Override
public void rampage() {
System.out.println("Robot Soldier " + serialNumber + " joins an army of robot soldiers to seize power in " + location+ " as part of a
plot to take over the world");
}
@Override
public String getOriginStory() {
return "evil robot soldier created by a mad scientist";
}
}
equals()
• We have already seen the String method equals()
• You can define a method that determines whether two objects of your
own class are equal. Often this tests whether all the data variables are
equal.
• If this method overrides the one from Object, it must take the same
argument type, namely another Object. This means that you could test
two objects of different types for equality. More likely, you will use a cast
to make sure you are comparing two objects of your class.
equals()
package demos;
public class Student {
private String name;
private Double gpa;
public Student(String nameIn, Double gpaIn) {
name = nameIn;
gpa = gpaIn;
}
public String toString() {
return "Name: " + name + "; GPA: " + gpa;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
Student otherStudent = (Student) obj;
if (name.equals(otherStudent.name) && gpa.equals(otherStudent.gpa)) return true;
return false;
}
}
equals()
package demos;
import java.util.ArrayList;
import java.util.List;
public class GradeBook {
public static void main(String[] args) {
// create an array list of Students
List<Student> students = new ArrayList<Student>();
String[] names = {"Skipper", "Gilligan", "Mary Anne", "Ginger", "Mr. Howell", "Mrs. Howell", "The Professor", "Mary Anne"};
double[] gpas = {2.7, 2.1, 3.9, 3.5, 3.4, 3.2, 4.0, 3.9};
Student currStudent;
for(int counter = 0; counter < names.length; counter++){
currStudent=new Student(names[counter], gpas[counter]);
students.add(currStudent);
}
testStudentsForEquality(students.get(0), students.get(4));
testStudentsForEquality(students.get(2), students.get(7));
testStudentsForEquality(students.get(3), students.get(3));
}
public static void testStudentsForEquality(Student s1, Student s2){
if(s1.equals(s2)) System.out.println(s1 + " = " + s2);
else System.out.println(s1 + " != " + s2);
}
}
CompareTo
• Java contains a way to make it easy to sort
objects in programmer-defined sort orders
• Collections.sort(list) is a static method of the
Collections class. Lists are a type of Collection.
• Sort() sorts according to the result of running
compareTo() when objects are compared during
the sort
• compareTo() compares the current object with
another one sent as input to the method
• compareTo() can compare objects using any
method you can code.
CompareTo
• Objects with compareTo() methods must be
declared to implement the interface
Comparable<>. Here is an example of the
interface declaration:
public class Student implements Comparable<Student>
• Note the parameterization of Comparable,
which looks just like the paramaterization
used when declaring a list. For now, use the
class name of the current class as the
parameter.
package demos;
CompareTo
public class Student implements Comparable<Student>{
private String name;
private Double gpa;
public Student(String nameIn, Double gpaIn){
name = nameIn;
gpa = gpaIn;
}
public String toString(){
return "Name: " + name + "; GPA: " + gpa;
}
// getters and setters omitted
@Override
public int compareTo(Student otherStudent) {
return this.gpa.compareTo(otherStudent.gpa);
}
}
CompareTo
package demos;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class GradeBook {
public static void main(String[] args) {
List<Student> students = new ArrayList<Student>();
String[] names = {"Skipper", "Gilligan", "Mary Anne", "Ginger", "Mr. Howell", "Mrs. Howell", "The Professor"};
double[] gpas = {2.7, 2.1, 3.9, 3.5, 3.4, 3.2, 4.0};
Student currStudent;
for(int counter = 0; counter < names.length; counter++){
currStudent=new Student(names[counter], gpas[counter]);
students.add(currStudent);
}
// output the data
System.out.println("Unsorted:");
for(Student s: students)
System.out.println(s);
Collections.sort(students);
System.out.println("\nSorted:");
for(Student s: students)
System.out.println(s);
}
}
Simulators
• Imagine you need data to test software that will be
used to process real-world measurements, like the
ages of college students
• Sometimes you might want linearly-distributed data,
but Gaussian (normal) distributions are often more
realistic
• Random.nextGaussian() returns a double from a
normal distribution with standard deviation = 1 and
mean = 0. Multiply it by the desired STD and then add
the desired mean
package simulator;
import java.util.Arrays;
public class Grader {
private double average;
private double std;
private int classSize;
private double[] grades;
private final double MINGRADE = 0;
private final double MAXGRADE = 100;
public enum GradingType {
LINEAR, GAUSSIAN
};
public Grader(double avgIn, double stdIn, int classSizeIn) {
average = avgIn;
std = stdIn;
classSize = classSizeIn;
}
public static void main(String[] args) {
Grader grd = new Grader(80d, 10d, 20);
grd.grade(GradingType.LINEAR);
grd.grade(GradingType.GAUSSIAN);
}
private void grade(GradingType type) {
Simulator sim = new Simulator();
if (type == GradingType.LINEAR)
grades = sim.getLinearData(classSize, MINGRADE, MAXGRADE);
if (type == GradingType.GAUSSIAN)
grades = sim.getGaussianData(average, std, classSize, MINGRADE,
MAXGRADE);
System.out.println("\nData using distribution type: " + type + "\n");
for (int i = 0; i < grades.length; i++) {
System.out.print("Student #" + i + " received a grade of ");
System.out.printf("%3.1f\n", grades[i]);
}
Arrays.sort(grades);
System.out.println("Here are the sorted values from the simulator:");
for (double d : grades)
System.out.printf("%3.1f\n",d);
}
}
package simulator;
import java.util.Random;
public class Simulator {
private static double[] nums;
public double[] getGaussianData(double mean, double std, int count, double min, double max) {
Random r = new Random();
nums = new double[count];
double randDoub;
for (int counter = 0; counter < nums.length; counter++){
randDoub = r.nextGaussian() * std + mean;
// it's pretty hard to set limits for the values in a good way, so here is a hacky way.
if(randDoub > max) randDoub = max;
if(randDoub < min) randDoub = min;
nums[counter] = randDoub;
}
return nums;
}
public double[] getLinearData(int count, double min, double max) {
// it would be better to make sure max < min first, but I am not implementing this in this example
Random r = new Random();
nums = new double[count];
double randDoub;
for (int counter = 0; counter < nums.length; counter++){
randDoub = r.nextDouble() * (max - min) + min;
nums[counter] = randDoub;
}
return nums;
}
}
Separate Things That Are Likely To Change Independently
• Grader is domain-specific (only useful for a narrow type of
problem, in this case grading schoolwork)
• Simulator, on the other hand, does not contain anything that
shows it is from an application that simulates grading. It could
be used to generate test data for a very wide variety of problems
• This is an example of an important principle in software
engineering. Separate the general from the specific, and
separate things you can probably reuse from things you can't. If
two things are likely to change independently of each other,
don’t combine them.
– "When all you have is a hammer, everything looks like a nail" – folk proverb
– "If I had a hammer, I'd hammer in the morning, I'd hammer in the evening, all over this land.
I'd hammer out danger; I'd hammer out a warning; I'd hammer out love between my
brothers and my sisters, all over this land." –Pete Seeger
Instanceof
• The next demo uses Java's instanceof operator, which
determines whether an object is an instance of a
particular class
• The syntax of instanceof may be counterintuitive; the
keyword does not use camel case and there are no
parentheses around the class name
• The next slide shows a simple demo of instanceof
Instanceof
package demos;
public class Demo {
Object myObject;
public void setMyObject(Object o) {
myObject = o;
}
public Object getMyObject() {
return myObject;
}
public static void main(String[] args) {
Demo demo = new Demo();
String newObject = new String("John");
demo.setMyObject(newObject);
Object o = demo.getMyObject();
if (o instanceof String)
System.out.println("\"" + o + "\"" + " is a String!");
else
System.out.println("not a String!");
Object arch = new Archimedes();
demo.setMyObject(arch);
Object o2 = demo.getMyObject();
if (o2 instanceof String)
System.out.println("\"" + 02 + "\"" + " is a String!");
else
System.out.println(o2 + " is not a String!");
}
}