Java arraylist add the qty - java

I am having an issue with comparing the properties of myProduct.setRefno(product.getRefno()), imported from another class along with a description, price and qty. I need to be able to key in a refno and if the reference number exists in the basket container, then I add only the qty rather than all the item details:
Currently program works as follows:
ref1 description1 price1 1(qty)
ref2 description2 price2 1(qty)
ref1 description1 price1 1(qty)
However I would like it as:
ref1 description1 price1 2(qty)
ref2 description2 price2 1(qty)
If it is the same refno then it adds only the qty.
public class Zapper {
public static void main(String[] args) throws ItemException {
System.out.println("\n\nThis is Purchases\n\n");
Products stock=new Products();// Define Variable
Products basket=new Products();// Define Variable
Purchase product;
String refno;
int offer;
int count;
int grandtotal;
char option;//char variable option
boolean finished=false;//variable "boolean" set
while (!finished) {
try {
option=Console.askOption("\n A)dd P)rint R)emove Q)uit");
stock.open("stock.lin");
switch (option) {
case 'A':
product= new Purchase();
refno= Console.askString("Enter Ref No:..");
product=(Purchase)stock.find(refno);//cast operator
if ( product == null) {
System.out.println("Cannot find Ref No");
} else {
product.print("");
Purchase myProduct = new Purchase();
myProduct.setRefno(product.getRefno());
myProduct.setDescription(product.getDescription());
myProduct.setPrice(product.getPrice());
myProduct.setQty(1);
myProduct.setOffer(product.getOffer());
basket.add(myProduct);//add the value of item into Container stock
}
break;//end of case statement Add
case 'R':
refno= Console.askString("Enter Ref No:..");
Product myProduct = new Product();
myProduct=(Purchase)basket.find(refno);//cast operator
myProduct.setQty(1);
if ( myProduct == null)
System.out.println("Cannot find Ref No");
else {
basket.remove(myProduct);
basket.print("");
}
break;//end of case statement Find
case 'P':
basket.print("\nYou have purchased...");
break;//end of case statement Print
case 'Q':
finished=true;
break;//end of case statement "Q"
case '\0':/*Do Nothing*/
break;//end of case statement "Do Nothing"
default:
System.out.println("Error: Invalid Option ");
break;//end of case statement default
}
} catch (ItemException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
System.out.println("\n\nPurchases Finished \n\n");
}
}

You only have to change the add-method in your Products class. Supposing you store your Purchase-objects in a List in your Product class, it could look something like this:
private List<Purchase> products;
public void add(Purchase product) {
String refNo = product.getRefno();
for (Purchase p : this.products) { //for every product
if (p.getRefno().equals(refNo)) { //if two refNumbers equals
p.setQty(p.getQty() + product.getQty()); //add the desired quantity
return; //force method to abort
}
}
this.products.add(product); //otherwise, add the new product
}
Although, I have to say I find some of your class-namings a bit unusual. Remember they should always give a good hint on what they actually represent, eg your Purchase class looks more like a Product. :)

A lot of your problems will fall away if you use some proper OOP coding techniques.
Lets start with the structure of your class.
public class Zapper {
public static void main(String[] args) throws ItemException {
System.out.println("\n\nThis is Purchases\n\n");
Products stock=new Products();// Define Variable
Products basket=new Products();// Define Variable
Purchase product;
String refno;
int offer;
int count;
int grandtotal;
char option;//char variable option
//Do the work...
}
}
First of all, in no case should main throw an Exception, nevermind a particular one like ItemException. All such things should be handled gracefully by the program. Secondly, you instantiate a bunch of objects that really should be held as member fields within the class, Zapper.
This might be more in tune with what you want:
public class Zapper {
//These things will stay throughout the program
private Products stock = new Products();
private Products basket = new Products();
private Purchase product;
private boolean quitSignalReceived = false;
private Set<Option> options;//A list of keyboard inputs
public static void main(String[] args) {
System.out.println("\n\nInitiating Purchase of items\n\n"); //use a proper entrance message to your program
Zapper zapper = new Zapper();//Create an object! Sets up all class variables on construction.
zapper.run();//Let a method handle running the program.
System.out.println("\n\nPurchases Finished \n\n");//Say goodbye!
}
public void run() {
//actually does the work!
}
}
Now you just need to focus on what run() does. Specifically, it handles your 'main loop':
public void run() {
boolean finished = false;
stock.open("stock.lin"); //Should happen every time we enter the run() loop
while (!finished) {
option=Console.askOption("\n A)dd P)rint R)emove Q)uit");
processOption(option);
if (quitSignalReceived) {
//Do anything that MUST happen before the program quits.
finished = true;
}
}
}
You've astutely noticed at this point we need to add Options in and process them.
public Zapper() {
this.options.add(new AddOption());
this.options.add(new QuitOption());
//etc.
}
public class Option {
//Constructor, etc.
process(String option) {
for (Option o : options) {
if (o.getKey.equals(option)) {
o.process(this);
}
}
}
}
This will naturally require an abstract class, Option, that you subclass:
public abstract class Option {
public String getKey();//returns the keyboard key associated with this option
public void process(Zapper z); //actually does what you need the option to do.
}
Let us consider your 'A' case:
case 'A':
product= new Purchase();
refno= Console.askString("Enter Ref No:..");
product=(Purchase)stock.find(refno);//cast operator
if ( product == null) {
System.out.println("Cannot find Ref No");
} else {
product.print("");
Purchase myProduct = new Purchase();
myProduct.setRefno(product.getRefno());
myProduct.setDescription(product.getDescription());
myProduct.setPrice(product.getPrice());
myProduct.setQty(1);
myProduct.setOffer(product.getOffer());
basket.add(myProduct);//add the value of item into Container stock
}
break;//end of case statement Add
First of all, you new() your product, which instantiates an object. Two lines later you set the variable to another object altogether. Secondly, you get that object from a method that does not return a Purchase object, which you should avoid at all cost and at the least encapsulate against future change. Then you have an if switch against null - which is another practice you should always avoid.
In the case of adding a product you want the process method of your Option subclass to look like below.
public void process (Zapper zap) {
refno= Console.askString("Enter Ref No:..");
Purchase stockItem;
bool success = zap.getPurchase(refno, item);
if ( !success ) {
System.out.println("Item not in stock.");//Errors should make sense!
} else {
zap.addToBasket(stockItem);
}
}
This requires adding the following methods to Zapper:
public bool findPurchase(String refno, Purchase item) {
item = this.stock.find(refno);
if (item == null) { return false; }
}
public void addToBasket(Purchase item) {
//actually do the work to add it to your purchases.
}
And to Products:
//This method copies the object into a new object and returns it.
public Purchase getPurchaseItem() {
Purchase myProduct = new Purchase();
myProduct.setRefno(product.getRefno());
myProduct.setDescription(product.getDescription());
myProduct.setPrice(product.getPrice());
myProduct.setQty(1);
myProduct.setOffer(product.getOffer());
}
Now, note a tonne of work is done for you if you change this:
private Products stock = new Products();
private Products basket = new Products();
To this:
private Map stock = new HashMap();
private Map basket = new HashMap();
In this case your call to increment-or-add would look like this:
if (basket.containsKey(item)){
int quantity = basket.get(item) + 1;
basket.set(item, quantity);
} else {
basket.set(item, 1);
}
While this is long it touches on many of the ways you can clean up this code, put responsibility where it belongs and where it is easiest, and solve your problem. If the Map class is insufficent to your needs, you might consider a situation where you have an Item class that comprises an item in stock and a PurchasedItem class that extends that, which is the thing you buy. That's a whole other discussion.

Related

Can I check the local vairable by using method in other class?

public class BookstoreRun {
public static void main(String[] args) {
BookstoreMenu bm = new BookstoreMenu();
bm.mainMenu();
}
}
Here's the menu class:
public class BookstoreMenu {
private Scanner sc = new Scanner(System.in);
private BookstoreController bc = new BookstoreController();
public void mainMenu() {
System.out.println("1. SignUp");
System.out.println("2. Check members list");
System.out.println("Select menu : ");
int menu = sc.nextInt();
switch (menu) {
case 1: {
bc.createAccount();
break;
} case 2:
default:
System.out.println("...");
}
}
}
This is controller class where I made methods:
public class BookstoreController {
private Scanner sc = new Scanner(System.in);
public void createAccount() {
System.out.println("Let's get started");
System.out.print("Your name : ");
String[] strArray = new String[0];
String name = sc.nextLine();
strArray = saveId(strArray, name);
System.out.print(name + ", Nice to meet you!");
System.out.println(Arrays.toString(strArray));
}
public String[] saveId(String[] originArr, String name) {
String[] newArr = new String[originArr.length + 1];
System.arraycopy(originArr, 0, newArr, 0, originArr.length);
newArr[originArr.length] = name;
return newArr;
}
}
I'm trying to make a menu with just two options. The first option is Sign Up through createAccount(); and once I finish signing up, I want to go back to the menu class and choose option 2.
I was thinking I could approach the information of strArray in BookstoreController class by typing bc.~ under case 2 of the switch in the BookstoreMenu class, but I failed.
My question is: Is it possible to approach the value which was made in the local area of another class?
No you cannot. Welcome to the world of Object Oriented Programming OOP & design. One of the more important ideas of OOP is that you encapsulate data and then access it through method calls (or, for other languages, properties).
In this case you should return an Account class from createAccount(). Then you can have a method there to the strArray. That variable should be a field in the Account class and be renamed to something that reflects its purpose, rather than the types it is made up of (string and arrays).
Now, in modern Java, we store objects like accounts in lists, not arrays. Lists can be grown at your leisure. I've put the list into a field of the controller, so it can be maintained in the right controlled location.
Here is some example:
public class BookstoreRun {
public static void main(String[] args) {
BookstoreMenu bm = new BookstoreMenu();
bm.mainMenu(new Scanner(System.in), System.out);
}
}
public class BookstoreMenu {
private BookstoreController bc = new BookstoreController();
public void mainMenu(Scanner sc, PrintStream out) {
while (true) {
// this is a "try with resources", using a localized scanner
int menu;
out.println("1. SignUp");
out.println("2. Check members list");
out.println("9. Quit");
out.println("Select menu : ");
menu = sc.nextInt();
// either menu has been assigned, or an exception has been thrown, so we can now use it
switch (menu) {
case 1:
bc.createAccount(sc, out);
break;
case 2:
bc.displayAccounts(out);
break;
// always leave yourself an exit option
case 9:
out.println("Bye");
System.exit(0);
// the default should display an error or warning
default:
out.println("Unknown option, try again");
}
}
}
}
public class BookstoreController {
// the list of accounts that is initially empty, but may grow
private List<Account> accounts = new ArrayList<Account>();
public void createAccount(Scanner sc, PrintStream out) {
out.println("Let's get started");
out.println("Your name : ");
String name = sc.nextLine();
out.println(name + ", nice to meet you!");
Account account = new Account(name);
accounts.add(account);
}
public void displayAccounts(PrintStream out) {
for (Account account : accounts) {
out.println(account);
}
}
}
// this is the additional "data class"
public class Account {
private String name;
// constructor that assigns the name to the field
public Account(String name) {
this.name = name;
}
// a method to retrieve the property name
public String name() {
return name;
}
// this is what is called when it is printed using println (converted to string)
#Override
public String toString() {
return String.format("Account %s", name);
}
}

How to save user choice into new array list

I’m sure I wasn’t clear enough in the title, clarifying everything here. I have a passenger booking a flight, I want to make a list in which the flights booked by the passenger will be stored.
This is the part of the code where the passenger completes the reservation, how do I make a new list and add a booked flight in it
#Override
public void payingFirstClassWithoutPromoCode(ArrayList<Flight> flightsList) {
System.out.println("Confirm buying ticket: (yes or no)");
String confirmation = scanner.nextLine();
if (confirmation.equalsIgnoreCase("yes")) {
if (selectedPassenger.getBalance() >= selectedFlight.getPriceForFirstClass()) {
System.out.println("Successful reserved!");
selectedPassenger.setBalance(selectedPassenger.getBalance() - selectedFlight.getPriceForFirstClass());
System.out.println(selectedPassenger.getFirstName() + "'s new balance is: " + selectedPassenger.getBalance());
} else {
noFundsAvailable(flightsList);
}
} else if (confirmation.equalsIgnoreCase("no")) {
cancellation(flightsList);
} else {
System.out.println("Wrong input!");
}
}
I think you can add a Flight field in your Passenger class, then do
selectedPassenger.setFlight(selectedFlight)
Here's the passenger class
class Passenger{
//other fields
Flight flight;
public void setFlight(Flight f){
this.flight = f;
}
If a passenger can have multiple flight, then declare a List in your passenger class, then do
selectedPassenger.getFlights().add(selectedFlight);
class Passenger{
//other fields
List<Flight> flight;
public Passenger(){
//don't forget to initialize flight list
flight = new ArrayList();
}
public List<Flight> getFlights(){
return this.flight ;
}
instead of fetching the whole list and adding it manually, you can have a method in your passenger class like
public void addFlight(Flight f){
this.flight.add(f);
}
then you can do
selectedPassenger.addFlight(selectedFlight);
You could design your Passenger class like this:
public class Passenger {
private String name;
private List<Flight> flights;
//Getter and setter for name
public Passenger(String name) {
this.name = name;
this.flights = new ArrayList<>();
}
public void addFlight(Flight flight) {
flights.add(flight);
}
}
On a side note, it might be worth refactoring your code. I note that you named your method payingFirstClassWithoutPromoCode(ArrayList<Flight> flightsList). I imagine you will have many more options, which means you will have to write multiple methods with very similar code.
It can be a good idea to make one method that can handle many different scenarios.
For example, you can add the ticket type (first/business/economy class) as an parameter in your method: payingWithoutPromoCode(ArrayList<Flight> flightsList, String ticketType).
It means you have to rewrite your method a little bit. In my example, you probably need to rewrite the methods in your Flight class. Instead of selectedFlight.getPriceForFirstClass() you can do selectedFlight.getPrice(ticketType).

toString won't show the expected result when displaying a node for a General Tree

I am creating a program that is able to have users create a name for a task list that will function as a root for my tree. The problem is, when displaying the node it would result to something like this:
===============CREATING A NEW TASK===============
Enter the name of the Task:
TaskOne
edu.slu.ds.RootTasks#76fb509a
edu.slu.ds.RootTasks#76fb509a
the second error should show "no children" as the user is just creating its root task.
I have created two methods that add the children nodes and display them once adding has been done. I have also had the toString that can return the data used with my display method:
Here is the code for the main program of creating tasks:
void CreateTask(){
System.out.println("===============CREATING A NEW TASK===============");
System.out.println("Enter the name of the Task: ");
String entered = keyboard.nextLine();
RootTasks task = new RootTasks(entered, null);
TreeNode<RootTasks> rootTasksTree =new TreeNode<>(task);
rootTasksTree.addChild(rootTasksTree);
rootTasksTree.showChildren();
System.out.println("Create a level one task Y/N?: ");
switch (keyboard.nextLine().toUpperCase()){
case "Y":
CreateLevelOneTask();
break;
case "N":
run();
break;
default:
System.out.println("Invalid Choice");
}
}
Here is my addChild and showChildren methods:
public void addChild(TreeNode<T> testNode){
if (firstChild == null) {
setFirstChild(testNode);
} else {
TreeNode<T> pointer = firstChild;
while (pointer.getSibling() != null){
pointer = pointer.sibling;
}
pointer.setSibling(testNode);
}
testNode.setRoot(this);
}
public void showChildren(){
System.out.println(this.toString() + "\n");
if(firstChild == null){
System.out.println("\tNo Branches");
} else if (firstChild.sibling != null){
TreeNode<T> pointer = firstChild;
while (pointer.sibling != null) {
pointer.showChildren();
System.out.println();
pointer = pointer.sibling;
}
pointer.showChildren();
} else {
System.out.println(firstChild.toString());
}
}
#Override
public String toString() {
return getData() + "";
}
}
The RootTask class will be used in creating the tasks at first and I've set the second parameter to null because that is where I will set its children nodes.
class RootTasks extends Tree{
private String title; //RootTasks title
private Tree<LevelOneTask> lvlOneTask;
public RootTasks(String title, Tree<LevelOneTask> lvlOneTask){
this.title = title;
this.lvlOneTask = lvlOneTask;
}
String getTitle(){
return title;
}
void setTitle(){
this.title = title;
}
public Tree<LevelOneTask> getLvlOneTask(){
return lvlOneTask;
}
public void setLvlOneTask(Tree<LevelOneTask> lvlOneTask) {
this.lvlOneTask = lvlOneTask;
}
public void displayTaskList(){
try {
System.out.println(getTitle());
}catch (Exception e){
System.out.println("No tasks of any found");
}
}
The case below for LevelOneTasks works the same at the RootTasks except consisting more with title,description, its status (marked complete or not) and LevelTwoTasks which only consist of a title and its status.
I expect the output to be something like this:
===============CREATING A NEW TASK===============
Enter the name of the Task:
TaskOne
Created Task:
TaskOne
No Children Added
Your output is the HashCode of the Object you're trying to print. You need to Override the toString() method of the Object you're trying to print. TreeNodes do have some getter-Methods though, like getUserObject().
The problem you're encountering stems from trying to call toString() on an Object of class Object, which like I said returns the HashCode of the Object instead of it's text value for example.

Need design suggestions for nested conditions

I need to write the logic with many conditions(up to 30 conditions) in one set of rule with many if else conditions and it could end in between or after all the conditions.
Here is the sample code I have tried with some possible scenario. This gives me result but doesn't look good and any minor miss in one condition would take forever to track.
What I have tried so far is, Take out common conditions and refactored to some methods. Tried creating interface with conditions and various set would implement it.
If you have any suggestion to design this, would help me. Not looking for detailed solution but even a hint would be great.
private Boolean RunCondition(Input input) {
Boolean ret=false;
//First if
if(input.a.equals("v1")){
//Somelogic1();
//Second if
if(input.b.equals("v2"))
//Third if
if(input.c >1)
//Fourth if
//Somelogic2();
//Go fetch key Z1 from database and see if d matches.
if(input.d.equals("Z1"))
System.out.println("Passed 1");
// Fourth Else
else{
System.out.println("Failed at fourth");
}
//Third Else
else{
if(input.aa.equals("v2"))
System.out.println("Failed at third");
}
//Second Else
else{
if(input.bb.equals("v2"))
System.out.println("Failed at second");
}
}
//First Else
else{
if(input.cc.equals("v2"))
System.out.println("Failed aat first");
}
return ret;
}
public class Input {
String a;
String b;
int c;
String d;
String e;
String aa;
String bb;
String cc;
String dd;
String ee;
}
The flow is complicated because you have a normal flow, plus many possible exception flows when some of the values are exceptional (e.g. invalid).
This is a perfect candidate to be handled using a try/catch/finally block.
Your program can be rewritten into following:
private Boolean RunCondition(Input input) {
Boolean ret=false;
try {
//First if
if(!input.a.equals("v1")) {
throw new ValidationException("Failed aat first");
}
//Somelogic1();
//Second if
if(!input.b.equals("v2")) {
throw new ValidationException("Failed at second");
}
//Somelogic2()
//Third if
if(input.c<=1) {
throw new ValidationException("Failed at third");
}
//Fourth if
//Somelogic2();
//Go fetch key Z1 from database and see if d matches.
if(!input.d.equals("Z1")) {
throw new ValidationException("Failed at fourth");
}
System.out.println("Passed 1");
} catch (ValidationException e) {
System.out.println(e.getMessage());
}
return ret;
}
Where you can define your own ValidationException (like below), or you can reuse some of the existing standard exception such as RuntimeException
class ValidationException extends RuntimeException {
public ValidationException(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
/**
*
*/
private static final long serialVersionUID = 1L;
}
You can read more about this in
https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
Make a separate class for the condition:
package com.foo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class App
{
static class Condition<T> {
final int idx;
final T compareValue;
public Condition(final int idx, final T compareValue) {
this.idx = idx;
this.compareValue = compareValue;
}
boolean satisfies(final T other) {
return other.equals(compareValue);
}
int getIdx() {
return idx;
}
}
public static void main( String[] args )
{
final List<Condition<String>> conditions = new ArrayList<Condition<String>>();
conditions.add(new Condition<String>(1, "v1"));
conditions.add(new Condition<String>(2, "v2"));
final List<String> inputs = new ArrayList<String>(Arrays.asList("v1", "xyz"));
boolean ret = true;
for (int i = 0; i < inputs.size(); i++) {
if (!conditions.get(i).satisfies(inputs.get(i)))
{
System.out.println("failed at " + conditions.get(i).getIdx());
ret = false;
break;
}
}
System.out.println("ret=" + ret);
}
}
#leeyuiwah's answer has a clear structure of the conditional logic, but exceptions aren't the right tool for the job here.
You shouldn't use exceptions to cope with non-exceptional conditions. For one thing, exceptions are really expensive to construct, because you have to walk all the way up the call stack to construct the stack trace; but you don't need the stack trace at all.
Check out Effective Java 2nd Ed Item 57: "Use exceptions only for exceptional conditions" for a detailed discussion of why you shouldn't use exceptions like this.
A simpler option is to define a little helper method:
private static boolean printAndReturnFalse(String message) {
System.out.println(message);
return false;
}
Then:
if(!input.a.equals("v1")) {
return printAndReturnFalse("Failed aat first");
}
// etc.
which I think is a simpler; and it'll be a lot faster.
Think of each rule check as an object, or as a Strategy that returns whether or not the rule passes. Each check should implement the same IRuleCheck interface and return a RuleCheckResult, which indicates if the check passed or the reason for failure.
public interface IRuleCheck
{
public RuleCheckResult Check(Input input);
public String Name();
}
public class RuleCheckResult
{
private String _errorMessage;
public RuleCheckResult(){}//All Good
public RuleCheckResult(String errorMessage)
{
_errorMessage = errorMessage;
}
public string ErrorMessage()
{
return _errorMessage;
}
public Boolean Passed()
{
return _errorMessage == null || _errorMessage.isEmpty();
}
}
public class CheckOne implements IRuleCheck
{
public RuleCheckResult Check(Input input)
{
if (input.d.equals("Z1"))
{
return new RuleCheckResult();//passed
}
return new RuleCheckResult("d did not equal z1");
}
public String Name();
}
Then you can simply build a list of rules and loop through them,
and either jump out when one fails, or compile a list of failures.
for (IRuleCheck check : checkList)
{
System.out.println("checking: " + check.Name());
RuleCheckResult result = check.Check(input);
if(!result.Passed())
{
System.out.println("FAILED: " + check.Name()+ " - " + result.ErrorMessage());
//either jump out and return result or add it to failure list to return later.
}
}
And the advantage of using the interface is that the checks can be as complicated or simple as necessary, and you can create arbitrary lists for checking any combination of rules in any order.

How to use interface methods randomly?

I have an interface and these methods:
public interface Form {
public void setFirstName (String value);
public void setLastName (String value);
public void setGender (String value);
}
Can I call these methods randomly on an objet? Something like:
form.randomMethodFromFormInterface(String randomString);
Is it actually possible? Just to clarify, I would like to fillout the form randomly: sometimes just the last name, sometimes just the first name, sometimes just the gender.
Random rand = new Random();
switch (rand.nextInt(3)) {
case 0: myForm.setFirstName(myFirstName); break;
case 1: myForm.setLastName(myLastName); break;
case 2: myForm.setGender(myGender); break;
default: throw new IllegalStateException();
}
Couldn't you use Random to pick from 0-2, and then depending on that value call the corresponding method?
Could you make another method in the interface that generates a random number and calls a method based on that number? Although I would bet there's an easier way to do this than creating an interface for it.
Here a general way, using reflection:
private static Random r = new Random();
public static void callRandomMethod(Object target, Class<?> iface, Object ... arguments) {
List<Method> methods = findFittingMethods(iface, arguments);
Method m = methods.get(r.nextInt(methods.size()));
m.invoke(target, arguments);
}
public List<Method> findFittingMethods(Class<?> iface, Object ... arguments
Method[] allMethods = iface.getMethods();
List<Method> fittingMethods = new ArrayList<Method>();
findMethodLoop:
for(Method candidate : allMethods) {
Class<?>[] argumentTypes = candidate.getArguments();
if(argumentTypes.length != arguments.length) {
continue;
}
// check argument types
for(int i = 0; i < argumentTypes.length; i++) {
if(arguments[i] == null) {
if(argumentTypes[i].isPrimitive()) {
// null can't be passed to a primitive argument.
continue findMethodLoop;
}
else {
// ... but to every other argument type.
continue; // check next argument
}
}
if(argumentTypes[i].isInstance(arguments[i])) {
continue; // check next argument
}
if(argumentTypes[i].isPrimitive()) {
// hack to check if we have the right wrapper class
try {
Array.set(Array.newInstance(argumentTypes[i], 1), 0, arguments[i]);
continue; // check next argument
}
catch(ArrayStoreException ex) {
continue findMethodLoop;
}
}
// wrong type
continue findMethodLoop;
}
// now we found a method which would accept the arguments, put it into the list.
fittingMethods.add(candidate);
}
return fittingMethods;
}
Of course, if you do this often, you would not create the list of methods for every call, but only once, and reuse it then. (And if you only have a known interface with a low number of methods, use the switch statement instead, like others have recommended.)
You can place the various method names in an array structure.
Then choose a random index within the scope of the array.
Then use reflection to actually call the method using the randomly chosen name from the previous step
Why not make the following method:
public static void randomMethodFromFormInterface(Form form, String value) {
switch(random.nextInt(3) {
case 0:
form.setFirstName(value);
break;
case 1:
form.setLastName(value);
break;
case 2:
form.setGenderName(value);
break;
}
}
You can put it in a utility class. random here is, of course, an instance of java.util.Random.
Can I call these methods randomly on
an objet?
Yes, this is possible with Reflection. The randomness is not implemented in this example (I assume that you can easily do this with a random int) and all methods are called without knowing how they are named or how many methods are available. For simplicity the example assumes that the parameter is only a String (like in your example). Of course, you must instantiate a class which implements Form:
Class thisClass = Class.forName("FormImpl");
Object o = thisClass.newInstance();
Method[] methods = thisClass.getDeclaredMethods();
for(Method m : methods)
{
m.invoke(o, "test");
}
You could do something like below. However, I am not sure if I really like the idea of calling methods in an interface randomly. It breaks the contract in a way and sounds like a bad design idea in my opinion.
import java.util.Random;
public class RandomInterfaceImpl implements RandomInterface {
private Random rnd;
public RandomInterfaceImpl(){
rnd = new Random();
}
#Override
public void setFirstName(String value) {
System.out.println("called setFirstName");
}
#Override
public void setLastName(String value) {
System.out.println("called setLastName");
}
#Override
public void setGender(String value) {
System.out.println("called setGender");
}
#Override
public void getNextRandomMethod(String value) {
int nextRand = rnd.nextInt(3);
switch(nextRand){
case 0: setFirstName(value); break;
case 1: setLastName(value); break;
case 2: setGender(value); break;
}
}
}
public static void main(String[] args) {
RandomInterface myInterface = new RandomInterfaceImpl();
myInterface.getNextRandomMethod("Foo");
myInterface.getNextRandomMethod("Foo");
myInterface.getNextRandomMethod("Foo");
}
prints:-
called setFirstName
called setLastName
called setLastName

Categories