Practice Session 5

advertisement
Practice Session 5
•
Java:
•
•
•
•
•
•
•
•
•
•
Packages
Collection Classes
Iterators
Generics
Default Methods
Anonymous Classes
Generic Methods
Lambdas
Design by Contract
JUnit
Java Packages
• What is Package?
– Way to group related class(es) and interface(s) into one unit
– Package is basically a folder.
• Benefits?
– Code organization
• All classes/interfaces related to one topic, are put under same package name.
– Allows us to resolve conflicts between class names
• By splitting the same name classes into two different packages.
– Controlling class visibility
• protected classes are visible to other classes inside the package only
• Not visible to other packages, when imported.
• Declaration:
Java Packages
– Declared at beginning of class file.
– Syntax: package PackageName;
• Example:
MyTest
Print1.java
Outs2
Outs1
• Folder: Package MyTest
– Sub-folder: Package Outs1
• File: Class Print1.java
• File: Class Print2java
– Sub-folder: Package Outs2
• File: Class Print1.java
Print2.java
Print1.java
Java Packages
• How to use them?
– Use of absolute path of class:
• Example: MyTest.Outs1.Print1 p1;
– Using import keyword:
• Import class:
import MyTest.Outs1.Print1;
• Import all classes under package:
import MyTest.Outs1.*;
• Object declaration: Print1 p1;
Java Collection
• What is it?
– A (huge)package of classes:
• import java.util.Collection;
– Contains useful data structures:
•
•
•
•
•
•
•
LinkedList
Stack
ArrayList
Vector
TreeSet
PriorityQueue
And much more…
Full API: http://docs.oracle.com/javase/6/docs/api/java/util/Collection.html
Collection Iterator
• All java.util.Collection data structures return an iterator to their
elements.
• Iterator commands:
– boolean hasNext()
• Returns true if there is a next element
– Object next()
• Returns the next object element/advances the iterator
– void remove()
• Optional operator / not all iterators implement it
• Removes the element returned by next
Using the Iterator
• Example:
– Print all Collection object elements:
• Code:
static void printAll (Collection<Integer> coll) {
Iterator<Integer> iter = coll.iterator( ); //returns iterator
while (iter.hasNext( )) { //checks if has next
System.out.println(iter.next( ) ); //returns object
}
}
Default Methods – Java 1.8 Feature
• Default methods enable us to add new functionalities to
interfaces without breaking the classes that implement that
interface.
• Allows us to implement methods directly in the interface!
• We add default keyword before the access modifier of the
method we wish to implement.
• This implementation will be the default implementation for all
classes implementing this interface, and did not override it.
Default methods tutorial: https://blog.idrsolutions.com/2015/01/java-8-default-methods-explained-5-minutes/
Default Method - Example
interface Stack<E> {
void push(E something);
E pop();
int size();
boolean isEmpty();
}
interface Stack<E> {
void push(E something);
E pop();
int size();
default boolean isEmpty(){
return (size())==0);
}
}
• Implementation of isEmpty() wil act as the default implementation for all
implementing classes, as long as the class does not overwrite the
implementation.
• If the same default method is implemented in two different interfaces, and
our class extends both interfaces, we must overwrite the default method.
Generics
• What are they?
– Generic data structures are data structures that allows the use of any object
type.
– Explicitly declare what object type the data structure is going to use upon
declaration, to help finding errors at compilation time.
• How?
– java.util.Collection.Stack<Object-Type> stk;
• Examples:
– A Stack that holds Integer objects.
• java.util.Stack<Integer> intStack;
– A LinkedList holding Student objects:
• java.util.Collection.LinkedList<Student> studentList;
Generics Information: https://docs.oracle.com/javase/tutorial/collections/index.html
Generics Example
Stack<Cow> stackOfCows = new Stack<Cow>()
Stack<Integer> stackOfInteger = new Stack<Integer>()
Cow cow = new Cow()
stackOfCows.push(cow);
stackOfInteger.push(cow);
Generic Methods – Java 1.8 Feature
public class Cow {
private int age;
private int weight;
public Cow(int age, int weight) {
this.age = age;
this.weight = weight;
}
public int getAge() {
return age;
}
public int getWeight() {
return weight;
}
}
Given an array of Cow objects what is the maximal object?
• Is it the heaviest cow?
• Maybe the oldest?
It depends on how you want to compare the cows!
Generic Methods
• In addition to generic classes and interfaces Java also supports
generic methods.
• Assume that we want to create a utility method for finding the
largest item in an array, like before we can have arrays of
Integers, Cows, and more.
• Let’s define a generic method!
Generic Methods Implementation
public static <T> T max(T[] array, Comparator<T> comparator) {
if (array.length == 0)
throw new IllegalArgumentException("empty array");
int maxIndex=0;
for (int i=1; i<array.length; i++)
if (comparator.compare(array[maxIndex], array[i]) < 0)
maxIndex = i;
return array[maxIndex];
}
• Note the <T> in front of the method signature which
parametrize the method.
Using Generic Methods
public static class CowComparatorByAge implements Comparator<Cow> {
public int compare(Cow o1, Cow o2) {
return o1.getAge() - o2.getAge();
}
}
public static class CowComparatorByWeight implements Comparator<Cow> {
public int compare(Cow o1, Cow o2) {
return o1.getWeight() - o2.getWeight();
}
}
public static void main(String[] args) {
Cow[] cows = {new Cow(7,50), new Cow(9,200), new Cow(3,100)};
System.out.println(max(ints, new CowComparatorByWeight ()).getWeight()); // 200
System.out.println(max(cows, new CowComparatorByAge()).getAge()); // 9
}
Anonymous Classes
• Anonymous classes enable you to make your code more
concise. ‫מתומצת‬
• They enable you to declare and instantiate a class at the same
time.
• They are like local classes except that they do not have a name.
• Use them if you need to use a local class once.
Anonymous Class Example
public static void main(String[] args) {
Cow[] cows = {new Cow(7,50), new Cow(9,200), new Cow(3,100)};
System.out.println(max(cows, new Comparator<Cow>(){
public int compare(Cow o1, Cow o2) {
return o1.getAge() - o2.getAge();
}
}).getAge()); // prints 9
System.out.println(max(cows, new Comparator<Cow>(){
public int compare(Cow o1, Cow o2) {
return o1.getWeight() - o2.getWeight();
}
}).getWeight()); // prints 200 }
• In the previous example we implemented two classes:
– CowComparatorByWeight
– CowComparatorByAge
• And we have used them once!
• A good place to use anonymous classes here.
Lambdas – Java 1.8 Feature
• Using anonymous classes to implement interfaces of one
method only is an overkill.
• The syntax of such a class is not user friendly.
• Using lambdas , we can use lambdas instead of anonymous
classes which provides easy to implement as well as to
readable syntax.
Full material: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
Lambda Advantages
• Allows writing a method in the same place to be used.
• Especially useful in places where a method is being used only
once, and the method definition is short.
• Saves the effort of declaring and writing a separate method to
the containing class.
(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }
Examples
(int a, int b) -> {
return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };
Guide: http://viralpatel.net/blogs/lambda-expressions-java-tutorial/
Cows Example – Using Lambda
Lambda:
public static void main(String[]
Cow[] cows = {new Cow(7,50),
System.out.println(max(cows,
System.out.println(max(cows,
}
args) {
new Cow(9,200), new Cow(3,100)};
(Cow o1, Cow o2) -> { return o1.getAge() - o2.getAge(); }).getAge());
(Cow o1, Cow o2) -> { return o1.getWeight() - o2.getWeight(); }).getWeight());
Anonymous Classes:
public static void main(String[] args) {
Cow[] cows = {new Cow(7,50), new Cow(9,200), new Cow(3,100)};
System.out.println(max(cows, new Comparator<Cow>(){
public int compare(Cow o1, Cow o2) {
return o1.getAge() - o2.getAge();
}
}).getAge()); // prints 9
System.out.println(max(cows, new Comparator<Cow>(){
public int compare(Cow o1, Cow o2) {
return o1.getWeight() - o2.getWeight();
}
}).getWeight()); // prints 200 }
Design by Contract(DBC)
• What?
– An approach for designing software.
– Expresses contract between specification and implementation.
– An agreement between two parties: client and supplier.
• Why?
– Trustworthy documentation.
– Strong support for debugging.
– Facilitate code reuse.
• Condition Types in DbC:
– Preconditions
• Things that must be true before invoking a method
• No conditions on after invocation.
– Postconditions
• Things that must be true after invoking a method
• No conditions on before invocation
– Invariants
• things that must be true before and after any method is invoked
• Must be true immediately after construction.
Design by Contract Example
public interface MyCollection {
Precondition disallows null argument.
/**
* Remove obj from the collection
*
* @require !( obj == null )
*
* @ensure !contains( obj )
*/
public void remove( Object obj );
Postcondition varifies that method
removes ALL occurrences of obj
/**
* @invariant size() >= 0
*/
public interface Queue {
Assures queue size >= 0 always!
Design by Contract Principles
• Separate queries from commands
• Separate basic queries from derived queries.
• For each derived query, write a postcondition that specifies what result will be returned, in
terms of one or more basic queries.
• For each command, write a postcondition that specifies the value of every basic query.
• For every query and command, decide on a suitable precondition.
• Write invariants to define unchanging properties of objects.
PRINCIPLE 1: separate commands and
queries
Commands
public void add( Object o );
public void remove(); //just a command - no result
Queries
public int size();
public Object head();
public boolean isEmpty();
PRINCIPLE 2: separate basic
queries and derived queries
Basic queries
public int size();
public Object head();
The choice of basic
queries can change
as we develop
contracts
Derived queries
public boolean isEmpty();
-- we can derive isEmpty() from size()
PRINCIPLE 3: specify derived queries in
terms of basic queries
/**
* Is the queue empty?
*
* @ensure return == ( size() == 0 )
*/
public boolean isEmpty();
-- if we know the value of size(), we also know
-- the value of isEmpty()
PRINCIPLE 4: specify postcondition for commands in
terms of basic queries
/**
* The first element in the queue
*
* @require size() >= 1
* @ensure return == elements().get(0)
*/
public Object head();
postcondition uses
basic query .get() for
its implementation
PRINCIPLE 5: add preconditions where
appropriate
/**
* The first element in the queue
*
* @require size() >= 1
*/
public Object head();
It wouldn’t make the software better
to pretend that an empty queue has
a head element.
PRINCIPLE 6: specify invariant
properties
/**
* @invariant size() >= 0
*/
public interface Queue {
Aim for properties that:
- help readers build correct conceptual model
Junit - Assert methods
• Each assert method has parameters like these:
message, expected-value, actual-value
–
–
–
–
–
–
–
assertTrue(String message, Boolean test)
assertFalse(String message, Boolean test)
assertNull(String message, Object object)
assertNotNull(String message, Object object)
assertEquals(String message, Object expected, Object actual) (uses equals method)
assertSame(String message, Object expected, Object actual) (uses == operator)
assertNotSame(String message, Object expected, Object actual)
• Assert methods dealing with floating point numbers get an
additional argument, a tolerance. (for rounding)
• Each assert method has an equivalent version that does not take a
message – however, this use is not recommended because:
– messages helps documents the tests
– messages provide additional information when reading failure logs
setUp/tearDown
• setUp()/tearDown()
– Functions which will run before and after each test.
– Example usage:
• setUp(): creating new objects
• tearDown(): clearing data, resetting data.
– If self destruction is good enough, there is no need to implement it.
• setUpBeforeClass()/tearDownBeforeClass()
– functions which will run exactly once before and after all the tests.
– Can be used to initialize objects which will be used throughout the tests.
– Changes done in first test, can be used as input data for the upcoming one.
Call order of the functions
JUnit tests for Counter class
public class CounterTest {
Counter counter1;
public CounterTest() { } // default constructor
@Before
protected void setUp() { // creates a (simple) test fixture
counter1 = new Counter();
}
}
@Test
public void testIncrement() {
assertTrue(counter1.increment() == 1);
assertTrue(counter1.increment() == 2);
}
@Test
public void testDecrement() {
assertTrue(counter1.decrement() == -1);
}
Note that each test begins
with a brand new counter
This means you don’t
have to worry about the
order in which the tests
are run
Junit - Example
• Interface:
package spl.util;
/**
* All objects are of type T. Contents are ordered in Last-In-First-Out order.
*/
public interface Stack<T> {
/**
* add the object at the top of the stack. (This is a command.)
* @param obj
*
any non null T object to be added to the stack
* @pre: none.
* @post: this.isEmpty()==false
* @post: this.pop(); this==@pre(this)
* @post: this.pop()== @param obj
*/
void push(T obj);
/**
* remove the top object from the stack and returns it.
* @return the topmost object on the stack
* @throws Exception in case the stack is empty
* @pre: this.isEmpty()== false;
* @post: none.
*/
T pop() throws Exception;
/**
* @return True if the Stack is empty, or False if the Stack contains at
*
least one {@link Object}.
* @pre: none.
* @post: none.
*/
boolean isEmpty();
}
Create Class StackImpl
package spl.util;
public class StackImpl<T> implements Stack<T> {
public void push(T obj) {
}
public T pop() throws Exception {
return null;
}
public boolean isEmpty() {
return false;
}
}
Creating a Test Case
package spl.util;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class StackImplTest {
Stack<Integer> stack;
Before -> run before each test
After-> run after each test
@Before
public void setUp() throws Exception {
this.stack = new StackImpl<Integer>();
}
@After
public void tearDown() throws Exception {
}
@Test
public void testPush() {
fail("Not yet implemented");
}
@Test
public void testPop() {
fail("Not yet implemented");
}
@Test
public void testIsEmpty() {
assertEquals(true, stack.isEmpty());
}
}
What happens when we run
the tests?
All 3 tests fail:
- testPush and testPop
because the methods are
not implemented.
- testIsEmpty since isEmpty
returns false, even though
the stack is empty.
• Add implementation to isEmpty() to pass the
tests.
• Implement other tests and add the
implementation of the methods they test to
pass them.
• Change the interface to include public void
remove() and public T top() in addition to
pop(), and implement pop() using remove
and top, to separate queries from commands.
• And so on….
Download