When can a method be called with less arguments than exist in the parameter list for the method?

I got an email asking for my opinion about when to use keyword arguments.

I thought I’d expand my answer into a full article about Ruby method arguments so everyone can benefit!

Ruby is very flexible when it comes to method arguments.

We have everything:

From the standard required arguments to optional arguments & even keyword (named) arguments.

In this article, you’re going to learn about the differences between all these argument types, and which ones to use depending on your situation.

Using the right kind of arguments will make your code easier to read & easier to work with.

When in Doubt Use Standard Arguments

Required arguments are what you get by default.

Here’ an example:

def write(file, data, mode) end write("cats.txt", "cats are cool!", "w")

If you don’t pass the exact number of arguments required you’ll get this familiar error message:

write("shopping_list.txt", "bacon") ArgumentError: wrong number of arguments (2 for 3)

This means that you gave the method 2 arguments, but it needs 3!

Flexible Methods With Optional Arguments

You may want to make some of your arguments optional by providing a default value.

For example:

def write(file, data, mode = "w") end

Now you can call write with 2 arguments, in which case mode will equal the default value ("w"), or you can pass in 3 arguments to override the default value & get different results.

This default value saves you some work when calling this method while still giving you the option to change it.

Use Keyword Arguments to Increase Clarity

One of the downsides of required & default arguments is that they are order dependent.

Meaning that you have to pass in the arguments in the same order

If you pass them in the wrong order:

You’ll get unexpected results!

For example, in the write method, you have to call it with the file first, then the data & finally the mode.

def write(file, data, mode) end

Keyword arguments allow you to vary the order of the arguments.

Here’s an example:

def write(file:, data:, mode: "ascii") end

This allows you to call the method in a different order:

write(data: 123, file: "test.txt")

But more importantly, when you call the method you can clearly see the meaning of the arguments.

I don’t find myself using keyword arguments very often, but they can be useful when you want to add clarity to method calls.

Let’s say that we have a Point class.

You could write it like this:

class Point def initialize(x: , y:) @x, @y = x, y end end point = Point.new(x: 10, y: 20)

This way there is no possible confusion about which of the two numbers (10 & 20) correspond to the x value or the y value.

That’s one of your main goals when writing code, to avoid confusion & misunderstanding as much as possible.

If using keyword arguments helps you do that then use them.

Think about this:

“Will adding keyword arguments make this easier to understand or will it add extra work & clutter to my code?”

Another thing about keyword arguments is that they are very explicit about the arguments you are missing.

Example:

point = Point.new # missing keywords: x, y

You can also combine keyword arguments with regular arguments.

One strategy I’ve been observing on Ruby built-in methods is that new versions tend to add new, optional arguments, as keyword arguments.

Examples of this are the keyword arguments for Float#round, Kernel#clone & String#lines introduced in Ruby 2.4.

Use Variable Arguments to Capture as Many Values as Necessary

What if you want to take an unlimited amount of values?

Then you can use variable arguments:

def print_all(*args) end print_all(1, 2, 3, 4, 5)

This allows you to call the method with any number of arguments, including none. A variable argument is an array with all the values passed to it.

You can combine this with other types of arguments.

Example:

def print_all(title, *chapters) end

This will take the 1st argument as title, and the rest as the chapters array. Notice that a variable argument needs to be after all your required & optional arguments, and before any keyword arguments.

Use The Correct Order

Here’s the valid order of arguments if you want to combine them & avoid a syntax error:

required -> optional -> variable -> keyword

Here’s a method that shows every possible argument type:

def testing(a, b = 1, *c, d: 1, **x) p a,b,c,d,x end testing('a', 'b', 'c', 'd', 'e', d: 2, x: 1)

The **x is the same as variable arguments, but for keyword arguments. It will be a hash instead of an array.

The Catch-All Argument

In some rare occasions you may see this:

def print_all(*) end

This means that the method is accepting any arguments, but it’s not doing anything with them. It’s similar to using the underscore character (_) inside a block to show which arguments you aren’t using.

A practical use for this could be in combination with the super keyword:

class Food def nutrition(vitamins, minerals) puts vitamins puts minerals end end class Bacon < Food def nutrition(*) super end end bacon = Bacon.new bacon.nutrition("B6", "Iron")

Now when Food’s nutrition changes its argument list, you don’t have to change arguments in Bacon too.

Summary

You’ve learned about Ruby’s powerful method arguments & how to use them! Required arguments are a good start, then you can add flexibility with optional arguments & add clarity with keyword arguments.

Hope you found this useful & interesting!

Share this post on Twitter so more people can see it 🙂

No, there is no standard guideline

But there are some techniques that can make a function with a lot of parameters more bearable.

You could use a list-if-args parameter (args*) or a dictionary-of-args parameter (kwargs**)

For instance, in python:

// Example definition def example_function(normalParam, args*, kwargs**): for i in args: print 'args' + i + ': ' + args[i] for key in kwargs: print 'keyword: %s: %s' % (key, kwargs[key]) somevar = kwargs.get('somevar','found') missingvar = kwargs.get('somevar','missing') print somevar print missingvar // Example usage example_function('normal parameter', 'args1', args2, somevar='value', missingvar='novalue')

Outputs:

args1 args2 somevar:value someothervar:novalue value missing

Or you could use object literal definition syntax

For example, here's a JavaScript jQuery call to launch an AJAX GET request:

$.ajax({ type: 'GET', url: 'http://someurl.com/feed', data: data, success: success(), error: error(), complete: complete(), dataType: 'jsonp' });

If you take a look at jQuery's ajax class there are a lot (approximately 30) more properties that can be set; mostly because ajax communications are very complex. Fortunately, the object literal syntax makes life easy.

C# intellisense provides active documentation of parameters so it's not uncommon to see very complex arrangements of overloaded methods.

Dynamically typed languages like python/javascript have no such capability, so it's a lot more common to see keyword arguments and object literal definitions.

I prefer object literal definitions (even in C#) for managing complex methods because you can explicitly see which properties are being set when an object is instantiated. You'll have to do a little more work to handle default arguments but in the long run your code will be a lot more readable. With object literal definitions you can break your dependence on documentation to understand what your code is doing at first glance.

IMHO, overloaded methods are highly overrated.

Note: If I remember right readonly access control should work for object literal constructors in C#. They essentially work the same as setting properties in the constructor.

If you have never written any non-trivial code in a dynamically typed (python) and/or functional/prototype javaScript based language, I highly suggest trying it out. It can be an enlightening experience.

It's can be scary first to break your reliance on parameters for the end-all, be-all approach to function/method initialization but you will learn to do so much more with your code without having to add unnecessary complexity.

Update:

I probably should have provided examples to demonstrate use in a statically typed language but I'm not currently thinking in a statically typed context. Basically, I've been doing too much work in a dynamically typed context to suddenly switch back.

What I do know is object literal definition syntax is completely possible in statically typed languages (at least in C# and Java) because I have used them before. In statically typed languages they're called 'Object Initializers'. Here are some links to show their use in Java and C#.

Get full access to Head First Java, 2nd Edition and 60K+ other titles, with free 10-day trial of O'Reilly.

There's also live online events, interactive content, certification prep materials, and more.

State affects behavior, behavior affects state. We know that objects have state and behavior, represented by instance variables and methods. But until now, we haven’t looked at how state and behavior are related. We already know that each instance of a class (each object of a particular type) can have its own unique values for its instance variables. Dog A can have a name “Fido” and a weight of 70 pounds. Dog B is “Killer” and weighs 9 pounds. And if the Dog class has a method makeNoise(), well, don’t you think a 70-pound dog barks a bit deeper than the little 9-pounder? (Assuming that annoying yippy sound can be considered a bark.) Fortunately, that’s the whole point of an object—it has behavior that acts on its state. In other words, methods use instance variable values. Like, “if dog is less than 14 pounds, make yippy sound, else...” or “increase weight by 5”. Let’s go change some state.

A class is the blueprint for an object. When you write a class, you’re describing how the JVM should make an object of that type. You already know that every object of that type can have different instance variable values. But what about the methods?

Well... sort of.*

Every instance of a particular class has the same methods, but the methods can behave differently based on the value of the instance variables.

The Song class has two instance variables, title and artist. The play() method plays a song, but the instance you call play() on will play the song represented by the value of the title instance variable for that instance. So, if you call the play() method on one instance you’ll hear the song “Politik”, while another instance plays “Darkstar”. The method code, however, is the same.

void play() { soundPlayer.playSound(title); }Song t2 = new Song(); t2.setArtist("Travis"); t2.setTitle("Sing"); Song s3 = new Song(); s3.setArtist("Sex Pistols"); s3.setTitle("My Way");

A small Dog’s bark is different from a big Dog’s bark.

The Dog class has an instance variable size, that the bark() method uses to decide what kind of bark sound to make.

Just as you expect from any programming language, you can pass values into your methods. You might, for example, want to tell a Dog object how many times to bark by calling:

d.bark(3);

Depending on your programming background and personal preferences, you might use the term arguments or perhaps parameters for the values passed into a method. Although there are formal computer science distinctions that people who wear lab coats and who will almost certainly not read this book, make, we have bigger fish to fry in this book. So you can call them whatever you like (arguments, donuts, hairballs, etc.) but we’re doing it like this:

A method uses parameters. A caller passes arguments.

Arguments are the things you pass into the methods. An argument (a value like 2, “Foo”, or a reference to a Dog) lands face-down into a... wait for it... parameter. And a parameter is nothing more than a local variable. A variable with a type and a name, that can be used inside the body of the method.

But here’s the important part: If a method takes a parameter, you must pass it something. And that something must be a value of the appropriate type.

Methods can return values. Every method is declared with a return type, but until now we’ve made all of our methods with a void return type, which means they don’t give anything back.

void go() { }

But we can declare a method to give a specific type of value back to the caller, such as:

int giveSecret() { return 42; }

If you declare a method to return a value, you must return a value of the declared type! (Or a value that is compatible with the declared type. We’ll get into that more when we talk about polymorphism in Chapter 7 and Chapter 8.)

Whatever you say you’ll give back, you better give back!

The bits representing 42 are returned from the giveSecret() method, and land in the variable named theSecret.

Methods can have multiple parameters. Separate them with commas when you declare them, and separate the arguments with commas when you pass them. Most importantly, if a method has parameters, you must pass arguments of the right type and order.

  1. Declare an int variable and assign it the value ‘7’. The bit pattern for 7 goes into the variable named x.

  2. Declare a method with an int parameter named z.

  3. Call the go() method, passing the variable x as the argument. The bits in x are copied, and the copy lands in z.

  4. Change the value of z inside the method. The value of x doesn’t change! The argument passed to the z parameter was only a copy of x.

    The method can’t change the bits that were in the calling variable x.

Reminder: Java cares about type!

You can’t return a Giraffe when the return type is declared as a Rabbit. Same thing with parameters. You can’t pass a Giraffe into a method that takes a Rabbit.

Now that we’ve seen how parameters and return types work, it’s time to put them to good use: Getters and Setters. If you’re into being all formal about it, you might prefer to call them Accessors and Mutators. But that’s a waste of perfectly good syllables. Besides, Getters and Setters fits the Java naming convention, so that’s what we’ll call them.

Getters and Setters let you, well, get and set things. Instance variable values, usually. A Getter’s sole purpose in life is to send back, as a return value, the value of whatever it is that particular Getter is supposed to be Getting. And by now, it’s probably no surprise that a Setter lives and breathes for the chance to take an argument value and use it to set the value of an instance variable.

class ElectricGuitar { String brand; int numOfPickups; boolean rockStarUsesIt; String getBrand() { return brand; } void setBrand(String aBrand) { brand = aBrand; } int getNumOfPickups() { return numOfPickups; } void setNumOfPickups(int num) { numOfPickups = num; } boolean getRockStarUsesIt() { return rockStarUsesIt; } void setRockStarUsesIt(boolean yesOrNo) { rockStarUsesIt = yesOrNo; } }

Until this most important moment, we’ve been committing one of the worst OO faux pas (and we’re not talking minor violation like showing up without the ‘B’ in BYOB). No, we’re talking Faux Pas with a capital ‘F’. And ‘P’.

Our shameful transgression?

Exposing our data!

Here we are, just humming along without a care in the world leaving our data out there for anyone to see and even touch.

You may have already experienced that vaguely unsettling feeling that comes with leaving your instance variables exposed.

Exposed means reachable with the dot operator, as in:

theCat.height = 27;

Think about this idea of using our remote control to make a direct change to the Cat object’s size instance variable. In the hands of the wrong person, a reference variable (remote control) is quite a dangerous weapon. Because what’s to prevent:

This would be a Bad Thing. We need to build setter methods for all the instance variables, and find a way to force other code to call the setters rather than access the data directly.

By forcing everybody to call a setter method, we can protect the cat from unacceptable size changes.

Yes it is that simple to go from an implementation that’s just begging for bad data to one that protects your data and protects your right to modify your implementation later.

OK, so how exactly do you hide the data? With the public and private access modifiers. You’re familiar with public–we use it with every main method.

Here’s an encapsulation starter rule of thumb (all standard disclaimers about rules of thumb are in effect): mark your instance variables private and provide public getters and setters for access control. When you have more design and coding savvy in Java, you will probably do things a little differently, but for now, this approach will keep you safe.

Mark instance variables private.

Mark getters and setters public.

“Sadly, Bill forgot to encapsulate his Cat class and ended up with a flat cat.”

(overheard at the water cooler).

Even though the methods don’t really add new functionality, the cool thing is that you can change your mind later. you can come back and make a method safer, faster, better.

Any place where a particular value can be used, a method call that returns that type can be used.

instead of:

int x = 3 + 24;

you can say:

int x = 3 + one.getSize();

Just like any other object. The only difference is how you get to them. In other words, how you get the remote control. Let’s try calling methods on Dog objects in an array.

  1. Declare and create a Dog array, to hold 7 Dog references.

    Dog[] pets; pets = new Dog[7];
  2. Create two new Dog objects, and assign them to the first two array elements.

    pets[0] = new Dog(); pets[1] = new Dog();
  3. Call methods on the two Dog objects.

    pets[0].setSize(30); int x = pets[0].getSize(); pets[1].setSize(8);

You already know that a variable declaration needs at least a name and a type:

int size; String name;

And you know that you can initialize (assign a value) to the variable at the same time:

int size = 420; String name = "Donny";

But when you don’t initialize an instance variable, what happens when you call a getter method? In other words, what is the value of an instance variable before you initialize it?

Instance variables always get a default value. If you don’t explicitly assign a value to an instance variable, or you don’t call a setter method, the instance variable still has a value!

You don’t have to initialize instance variables, because they always have a default value. Number primitives (including char) get 0, booleans get false, and object reference variables get null.

(Remember, null just means a remote control that isn’t controlling / programmed to anything. A reference, but no actual object.)

  1. Instance variables are declared inside a class but not within a method.

    class Horse { private double height = 15.2; private String breed; // more code... }
  2. Local variables are declared within a method.

    class AddThing { int a; int b = 12; public int add() { int total = a + b; return total; } }
  3. Local variables MUST be initialized before use!

Local variables do NOT get a default value! The compiler complains if you try to use a local variable before the variable is initialized.

Sometimes you want to know if two primitives are the same. That’s easy enough, just use the == operator. Sometimes you want to know if two reference variables refer to a single object on the heap. Easy as well, just use the == operator. But sometimes you want to know if two objects are equal. And for that, you need the .equals() method. The idea of equality for objects depends on the type of object. For example, if two different String objects have the same characters (say, “expeditious”), they are meaningfully equivalent, regardless of whether they are two distinct objects on the heap. But what about a Dog? Do you want to treat two Dogs as being equal if they happen to have the same size and weight? Probably not. So whether two different objects should be treated as equal depends on what makes sense for that particular object type. We’ll explore the notion of object equality again in later chapters (and Appendix B), but for now, we need to understand that the == operator is used only to compare the bits in two variables. What those bits represent doesn’t matter. The bits are either the same, or they’re not.

Use == to compare two primitives, or to see if two references refer to the same object.

Use the equals() method to see if two different objects are equal.

(Such as two different String objects that both represent the characters in “Fred”)

To compare two primitives, use the == operator

The == operator can be used to compare two variables of any kind, and it simply compares the bits.

if (a == b) {...} looks at the bits in a and b and returns true if the bit pattern is the same (although it doesn’t care about the size of the variable, so all the extra zeroes on the left end don’t matter).

int a = 3; byte b = 3; if (a == b) { // true }

To see if two references are the same (which means they refer to the same object on the heap) use the == operator

Remember, the == operator cares only about the pattern of bits in the variable. The rules are the same whether the variable is a reference or primitive. So the == operator returns true if two reference variables refer to the same object! In that case, we don’t know what the bit pattern is (because it’s dependent on the JVM, and hidden from us) but we do know that whatever it looks like, it will be the same for two references to a single object.

Foo a = new Foo(); Foo b = new Foo(); Foo c = a; if (a == b) { // false } if (a == c) { // true } if (b == c) { // false }

Get Head First Java, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience live online training, plus books, videos, and digital content from nearly 200 publishers.