Wednesday, May 6, 2026

Python continue Statement With Examples

The Python continue Statement is a loop control keyword used inside for and while loops to skip the rest of the current iteration and jump directly to the next cycle of the loop. When continue is encountered, Python immediately transfers control back to the beginning of the loop, ignoring any statements that follow within that iteration.

The common use case for continue statement is to pair it with if condition with in the loop to selectively skip certain iterations. When the condition is true the continue statement is executed resulting in next iteration of the loop.

continue statement Python examples

1. Using continue statement with for loop in Python. In the example a for loop in range 1..10 is executed and it prints only odd numbers.

for i in range(1, 10):
    # Completely divisble by 2 means even number
    # in that case continue with next iteration
    if i%2 == 0:
        continue
    print(i)

print('after loop')

Output

1
3
5
7
9
after loop

2. Using continue statement with while loop in Python. In the example while loop is iterated to print numbers 1..10 except numbers 5 and 6. In that case you can have an if condition to continue to next iteration when i is greater than 4 and less than 7.

i = 0
while i < 10:
    i += 1
    if i > 4 and i < 7:
        continue
    print(i)

Output

1
2
3
4
7
8
9
10

3. Here is another example of using continue statement with infinite while loop. In the example there is an infinite while loop that is used to prompt user for an input. Condition here is that entered number should be greater than 10, if entered number is not greater than 10 then continue the loop else break out of the loop.

while True:
    num = int(input("Enter a number greater than 10: "))
    # condition to continue loop
    if num < 10:
        print("Please enter a number greater than 10...")
        continue
    else:
        break

print("Entered number is", num)

Output

Enter a number greater than 10: 5
Please enter a number greater than 10...
Enter a number greater than 10: 16
Entered number is 16

That's all for this topic Python continue Statement With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. Python break Statement With Examples
  2. Python return Statement With Examples
  3. pass Statement in Python
  4. Encapsulation in Python
  5. Namespace And Variable Scope in Python

You may also like-

  1. Constructor in Python - __init__() function
  2. Strings in Python With Method Examples
  3. List in Python With Examples
  4. What Are JVM, JRE And JDK in Java
  5. Equality And Relational Operators in Java
  6. Lambda Expressions in Java 8
  7. What is Hadoop Distributed File System (HDFS)
  8. Circular Dependency in Spring Framework

Method Overloading in Java

Method Overloading in Java occurs when two or more methods in the same class (or in a parent‑child class hierarchy) share the same method name but differ in their parameter list, either by the number of parameters or their types.

For example:

void test(int a, int b) { ... }
void test(float a, int b) { ... }
void test(int a, int b, int c) { ... }

Here, the method name test is reused, but the parameter signatures differ, making them valid overloaded methods.


Method Overloading and Polymorphism

Method overloading is a classic example of compile‑time polymorphism (also called static polymorphism) in Java. The compiler determines which overloaded method to invoke based on the method signature at compile time, ensuring type safety and performance.

How Java Decides Which Overloaded Method to Call

When you invoke an overloaded method, the Java compiler determines which version to execute at compile time itself. This resolution process is based on the method signature, specifically the number and types of parameters provided in the call.

  • Types of the parameters- For example test(int a, int b) and test(float a, int b). In these overloaded methods name is same but the types of the method parameters differ.
  • Number of the parameters- For example test(int a, int b) and test(int a, int b, int c). In these overloaded methods name is same but the number of parameters is different.

Please note that return type of the methods may be different but that alone is not sufficient to determine the method that has to be called.

Examples of method overloading in Java

1. Method overloading example with methods having different types of parameters. In the Java example there are two overloaded methods, one having two int parameters where as the other method has one int and one double parameter.

public class OverloadingExample {
 // overloaded Method
 void overloadedMethod(int i, int j){
  System.out.println("In overloadedMethod with both int parameters- " + i);
 }
 
 // overloaded Method
 void overloadedMethod(int i, double j){
  System.out.println("In overloadedMethod with int and double parameters " + i + " " + j);
 }
 
 
 public static void main(String args[]){ 
  OverloadingExample obj = new OverloadingExample();
  obj.overloadedMethod(5, 7);
  obj.overloadedMethod(5, 103.78);
 }
}

Output

In overloadedMethod with both int parameters- 5
In overloadedMethod with int and double parameters 5 103.78

2. Method overloading example with methods having different number of parameters. In the Java example there are two overloaded methods, one having a single int parameter where as the other method has one int and one String parameter.

public class OverloadingExample {
 // overloaded Method
 void overloadedMethod(int i){
  System.out.println("In overloadedMethod with int parameter- " + i);
 }
 
 // overloaded Method
 void overloadedMethod(int i, String s){
  System.out.println("In overloadedMethod with int and string parameters- " + i + " " + s);
 }
 
 
 public static void main(String args[]){ 
  OverloadingExample obj = new OverloadingExample();
  obj.overloadedMethod(5);
  obj.overloadedMethod(5, "Test");
 }
}

Output

In overloadedMethod with int parameter- 5
In overloadedMethod with int and string parameters- 5 Test

Method overloading in Java and automatic type conversion

In Java, automatic type promotion may happen and it does have a role in how overloaded methods are called, let's see it with an example to have clarity.

public class OverloadingExample {
 // overloaded Method with 1 int param
 void overloadedMethod(int i){
  System.out.println("In overloadedMethod with one int parameter " + i);
 }
 
 // overloaded Method with 2 double params
 void overloadedMethod(double i, double j){
  System.out.println("In overloadedMethod with 2 double parameters " + i + " " + j);
 }
 
 
 public static void main(String args[]){ 
  OverloadingExample obj = new OverloadingExample();
  obj.overloadedMethod(5);
  obj.overloadedMethod(5.7, 103.78);
  obj.overloadedMethod(5, 10);
 }
}

Here notice the third call to the method
obj.overloadedMethod(5, 10);

It has 2 int params, but there is no overloaded method with 2 int params, in this case automatic type conversion happens and java promotes these 2 int parameters to double and calls overloadedMethod(double i, double j) instead.

Method Overloading in Java and Inheritance

In Java, when dealing with a parent‑child relationship (inheritance), the distinction between method overloading and method overriding becomes important.

  • Method Overriding occurs when the child class defines a method with the same name, same parameter list (number and types), and same return type (or a covariant type) as the parent class. In this case, the child's method overrides the parent's implementation, and the decision of which method to call is made at runtime (dynamic polymorphism).
  • Method Overloading, on the other hand, happens when the child class defines a method with the same name but a different parameter list (either in number or types) compared to the parent class. Here, the compiler treats them as distinct methods, and the resolution is done at compile time (static polymorphism).
class Parent {
 private int i;
 // Constructor
 Parent(int i){
  this.i = i;
 }
 // Method with no param
 public void dispayData(){
  System.out.println("Value of i " + i);
 } 
}

class Child extends Parent{
 private int j;
 // Constructor
 Child(int i, int j){
  // Calling parent class constructor
  super(i);
  this.j = j;
 }
 // Overloaded Method with String param
 public void dispayData(String showMsg){
  System.out.println(showMsg + "Value of j " + j);
 } 
}

class OverloadDemo{
 public static void main(String args[]){ 
  Child childObj = new Child(5, 10);
  // This call will invoke the child class method
  childObj.dispayData("in Child class displayData ");
  // This call will invoke the parent class method
  childObj.dispayData();
 }
}

Output of the program would be

in Child class displayData Value of j 10
Value of i 5

Benefits of Method Overloading

One of the biggest advantages of Method Overloading in Java is improved readability and code maintainability. Without overloading, developers would need to create multiple methods with different names for similar functionality, which quickly becomes confusing and clutters the code.

  1. Cleaner Code: Instead of writing separate methods like addInt(int a, int b) or addLong(long a, long b), you can simply use one method name add() with different parameter signatures.
  2. Improved Readability: Overloading allows logically related operations to share the same name, making the code easier to understand at a glance.
  3. Polymorphism Support: Method overloading is a form of compile‑time polymorphism, enabling the compiler to choose the correct method based on the arguments passed.

That's all for this topic Method Overloading in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Basics Tutorial Page


Related Topics

  1. Method Overriding in Java
  2. Polymorphism in Java
  3. Inheritance in Java
  4. Difference Between Encapsulation And Abstraction in Java
  5. Java OOP Interview Questions And Answers

You may also like-

  1. Object Creation Using new Operator in Java
  2. final Keyword in Java With Examples
  3. Java Abstract Class and Abstract Method
  4. Inter-thread Communication Using wait(), notify() And notifyAll() in Java
  5. How HashMap Works Internally in Java
  6. final Vs finally Vs finalize in Java
  7. Method Overloading in Python
  8. Dependency Injection in Spring Framework

StringBuilder Class in Java With Examples

The StringBuilder class in Java, introduced in Java 5, is a mutable (modifiable) sequence of characters just like StringBuffer class. Unlike the immutable String class in Java, where every modification creates a new object, StringBuilder allows in‑place changes to its content and length through various method calls. This makes it ideal for scenarios involving frequent string modifications.

Internally, a StringBuilder class object works like a dynamic, variable‑length array of characters.

StringBuilder vs StringBuffer

Both StringBuilder and StringBuffer share a similar API, but the key difference lies in thread safety:

  • StringBuffer is synchronized and thread‑safe, making it suitable for multi‑threaded environments.
  • StringBuilder is not thread‑safe, but this trade‑off delivers significantly better performance in single‑threaded applications.

As per the official Java documentation– StringBuilder class is designed for use as a drop-in replacement for StringBuffer in places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.


Constructors in Java StringBuilder class

  • StringBuilder()- Creates an empty builder with a default capacity of 16.
  • StringBuilder(CharSequence seq)- Constructs a string builder that contains the same characters as the specified CharSequence.
  • StringBuilder(int capacity)- Creates an empty builder with the specified initial capacity.
  • StringBuilder(String str)- Constructs a string builder initialized to the contents of the specified string.

Tuesday, May 5, 2026

CopyOnWriteArrayList in Java With Examples

This post talks about CopyOnWriteArrayList in Java which is part of the java.util.concurrent package. It is a specialized thread safe implementation of the List interface.


Synchronized List options in Java

Though we have an option to synchronize the collections like List or Set using Collections.synchronizedList() or Collections.synchronizedSet() methods of the Collections class but there is a drawback to this synchronization; very poor performance because-

  • The entire collection is locked during access using a single lock.
  • Only a single thread can access it at a given time leading to significant performance bottlenecks under heavy concurrency.

Java also has a legacy Vector class as a thread-safe alternative to List but that thread safety is achieved by synchronizing all the methods of the Vector class, which again results in poor performance.

CopyOnWriteArrayList in Java

Java 5 onwards, CopyOnWriteArrayList is introduced as a thread-safe variant of ArrayList. It is designed for concurrent access from multiple threads. CopyOnWriteArrayList provides a thread-safe alternative for ArrayList, same way ConcurrentHashMap provides a thread-safe alternative for HashMap and CopyOnWriteArraySet for HashSet.

Thread safety in CopyOnWriteArrayList

Thread safety in CopyOnWriteArrayList lies in its "copy-on-write" strategy:

  • Every mutative operation (add, set, remove) creates a new copy of the underlying array.
  • Readers always see a consistent snapshot of the list, unaffected by concurrent modifications.
  • This ensures lock-free reads and predictable iteration behavior.

You may argue that this way of creating a fresh copy whenever any mutative operation is performed must be very costly. Yes it is, that is why using CopyOnWriteArrayList provides better performance in use cases with frequent reads but infrequent writes.

That brings us to the second point "snapshot style" iterator in CopyOnWriteArrayList.

CopyOnWriteArrayList has a fail-safe iterator

The iterator returned by Java CopyOnWriteArrayList is fail-safe, it uses a reference to the state of the array at the point that the iterator was created. You know by now any mutation will result in a fresh copy of the underlying array. Thus the array that the iterator has a reference to never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException.

The iterator will not reflect additions, removals, or changes to the list since the iterator was created thus it is also known as "snapshot style" iterator.

Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.

Since iterator is not affected by the mutations thus multiple threads can iterate the collection without interference from one another or from threads wanting to modify the collection.

Java CopyOnWriteArrayList constructors

  • CopyOnWriteArrayList()- This constructor Creates an empty list.
  • CopyOnWriteArrayList​(E[] toCopyIn)- Creates a list holding a copy of the given array.
  • CopyOnWriteArrayList​(Collection<? extends E> c)- Creates a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.

CopyOnWriteArrayList Java Example

Let's see a simple Java example creating CopyOnWriteArrayList and adding elements to it.

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteALDemo {
  public static void main(String[] args) {
    List<String> numList = new CopyOnWriteArrayList<String>();
    numList.add("1");
    numList.add("2");
    numList.add("3");
    numList.add("4");
    // Displaying CopyOnWriteArrayList elements
    for(String num : numList){
      System.out.println("Number- " + num);
    }
  }
}

Output

Number- 1
Number- 2
Number- 3
Number- 4

Java CopyOnWriteArrayList iterator Example

Let's see "snapshot style" iterator concept of CopyOnWriteArrayList in Java with an example.

First let's use ArrayList with 2 threads accessing it concurrently. One of the thread tries to structurally modified the ArrayList while second thread is iterating it. This should result in a ConcurrentModificationException.

public class FailFastDemo {
  public static void main(String[] args) {
    List<String> numList = new ArrayList<String>();
    numList.add("1");
    numList.add("2");
    numList.add("3");
    numList.add("4");
        
    //This thread will iterate the list
    Thread thread1 = new Thread(){ 
      public void run(){ 
        try{ 
          Iterator<String> i = numList.iterator(); 
          while (i.hasNext()){ 
            System.out.println(i.next()); 
            // Using sleep to simulate concurrency
            Thread.sleep(1000); 
          }     
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread1 : Concurrent modification detected 
            on this list"); 
          e.printStackTrace();
        }catch(InterruptedException e){
          
        } 
      } 
    }; 
    thread1.start(); 
        
    // This thread will try to add to the collection,
    // while the collection is iterated by another thread.
    Thread thread2 = new Thread(){ 
      public void run(){ 
        try{ 
          // Using sleep to simulate concurrency
          Thread.sleep(2000);
          // adding new value to the shared list
          numList.add("5"); 
          System.out.println("new value added to the list"); 
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread2 : Concurrent modification detected 
                  on the List"); 
        } catch(InterruptedException e){}
      } 
    }; 
    thread2.start(); 
  }
}

Output

1
2
new value added to the list
thread1 : Concurrent modification detected on this list
java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
 at java.util.ArrayList$Itr.next(Unknown Source)
 at org.netjs.prog.FailFastDemo$1.run(FailFastDemo.java:24)

Here it can be seen that the ConcurrentModificationException is thrown because the list is changed by a thread while it has been iterated by another thread.

Now in the same code change the ArrayList to CopyOnWriteArrayList. Also added one sysout after adding new element to the list.

public class FailFastDemo {
  public static void main(String[] args) {
    List<String> numList = new CopyOnWriteArrayList<String>();
    numList.add("1");
    numList.add("2");
    numList.add("3");
    numList.add("4");
        
    //This thread will iterate the list
    Thread thread1 = new Thread(){ 
      public void run(){ 
        try{ 
          Iterator<String> i = numList.iterator(); 
          while (i.hasNext()){ 
            System.out.println(i.next()); 
            // Using sleep to simulate concurrency
            Thread.sleep(1000); 
          }     
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread1 : Concurrent modification detected 
            on this list"); 
          e.printStackTrace();
        }catch(InterruptedException e){
                    
        } 
      } 
    }; 
    thread1.start(); 
        
    // This thread will try to add to the collection,
    // while the collection is iterated by another thread.
    Thread thread2 = new Thread(){ 
      public void run(){ 
        try{ 
          // Using sleep to simulate concurrency
          Thread.sleep(2000);
          // adding new value to the shared list
          numList.add("5"); 
          System.out.println("new value added to the list"); 
          System.out.println("List " + numList);
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread2 : Concurrent modification detected 
           on the List"); 
        } catch(InterruptedException e){}
      } 
    }; 
    thread2.start();    
  }
}

Output

1
2
3
new value added to the list
List [1, 2, 3, 4, 5]
4

Here ConcurrentModificationException is not thrown as CopyOnWriteArrayList is used now. Also note that, though one of the thread adds a new element and at that time the list prints all the elements from 1-5. But the iterator has the reference to the old copy of the list and it prints from 1-4.

Points to note

  • CopyOnWriteArrayList in Java provides a thread-safe alternative to the normal ArrayList.
  • In CopyOnWriteArrayList thread safety is achieved in a different way from a thread safe collection like Vector. In CopyOnWriteArrayList fresh copy of the underlying array is created with every mutative operations (add, set, and so on).
  • Because of this approach CopyOnWriteArrayList gives better performance in case there are more threads iterating the list than mutating it. Several threads can iterate CopyOnWriteArrayList concurrently.
  • CopyOnWriteArrayList's iterator is fail-safe and guaranteed not to throw ConcurrentModificationException.
  • CopyOnWriteArrayList's iterator uses a reference to the state of the array at the point that the iterator was created.
  • The iterator will not reflect additions, removals, or changes to the list since the iterator was created thus it is also known as "snapshot style" iterator.
  • All elements are permitted, including null.

That's all for this topic CopyOnWriteArrayList in Java With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. ConcurrentHashMap in Java With Examples
  2. Java CyclicBarrier With Examples
  3. Difference Between ArrayList And CopyOnWriteArrayList in Java
  4. Fail-Fast Vs Fail-Safe Iterator in Java
  5. Java Concurrency Interview Questions And Answers

You may also like-

  1. How ArrayList Works Internally in Java
  2. Java Collections Interview Questions And Answers
  3. How to Iterate a HashMap of ArrayLists of String in Java
  4. Java Program to Convert a File to Byte Array
  5. static reference to the non-static method or field error
  6. Race Condition in Java Multi-Threading
  7. Java ThreadLocal Class With Examples
  8. Interface Default Methods in Java

Tool Calling in LangChain

In the previous article Tools in LangChain With Examples we saw how you can create tools in LangChain and how to invoke those tools using invoke() method of the tool as every tool is a Runnable in LangChain which means it automatically inherits the .invoke() method from the Runnable interface. That works great when you are the one calling the tool. But the real magic happens when you let the LLM itself decide which tool to use, that’s where tool calling in LangChain comes into picture.

LangChain’s tool calling

Tools aren’t just standalone functions which you will execute manually, they become extensions of the LLM’s reasoning. You bind tools with the model so it can choose the appropriate tool call to get the result.

Integrating tools with the LLM

In LangChain you can use bind_tools() method to integrate your custom tools with the LLM. With bind_tools() method you can pass the list of tools to bind to the model.

By passing a list of tools to bind_tools(), you expose their name, description, and input schema to the model. This allows the LLM to recognize when a user query matches a tool’s purpose and respond with a structured ToolMessage, essentially a request to execute that tool.

It’s important to understand that the LLM does not read or execute your code directly. When it recognizes that a user request matches a tool’s purpose, it sends back a ToolMessage like:

{"name": "multiply", "args": {"a": 7, "b": 8}}

This is a signal that the tool should be run, but the execution itself is your responsibility.

Developer’s Role:

After receiving the tool call, following are the activities that needs to be done manually:

  1. Parsing the ToolMessage from the LLM’s response.
  2. Executing the tool with the provided arguments.
  3. Returning the result back to the model so it can continue reasoning and produce a polished answer.

If you want the orchestration to happen automatically, you’ll need to use an agent. Agents handle the loop of:

  1. Detecting tool calls,
  2. Running the tools,
  3. Feeding results back into the LLM,
  4. Continuing the conversation seamlessly.

In this article we’ll stick to manual bind_tools() workflow where the developer explicitly handles tool execution after the LLM emits a tool call. This approach helps you understand what’s happening under the hood before moving on to full agent automation.

Let’s try to make bind_tools() workflow clear with an example, we’ll use a multiply tool that multiplies two numbers.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_groq import ChatGroq
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file

# Multiplication tool
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

llm_with_tools = llm.bind_tools([multiply])

# Now the LLM can decide when to call the tool
response = llm_with_tools.invoke("What is 3 times 7?")
print(response)

Full Output

content='' additional_kwargs={'tool_calls': [{'id': 'kj0h3cwtm', 'function': {'arguments': '{"a":3,"b":7}', 'name': 'multiply'}, 
'type': 'function'}]} response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 228, 'total_tokens': 247,
'completion_time': 0.058486455, 'completion_tokens_details': None, 'prompt_time': 0.034593443, 'prompt_tokens_details': None,
'queue_time': 0.159857826, 'total_time': 0.093079898}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_45180df409', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df212-a97c-7ab3-b5be-a48bdeb26b31-0' 
tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 7}, 'id': 'kj0h3cwtm', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 228, 'output_tokens': 19, 'total_tokens': 247}

If you inspect the output you will see a tool call, with name and args, is returned by the model.

tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 7}, 'id': 'kj0h3cwtm', 'type': 'tool_call'}]

But LangChain won’t automatically execute the tool for you, as LLM doesn’t actually read your code. You, as a developer need to parse this tools call and invoke the required tool.

Given below is the complete code.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_groq import ChatGroq
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file

# Multiplication tool
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

llm_with_tools = llm.bind_tools([multiply])

# Now the LLM can decide when to call the tool
response = llm_with_tools.invoke("What is 3 times 7?")
print(response)

# if tool call is requested, it will be in response.tool_calls
if response.tool_calls:
    # loop through tool calls and execute them
    for tc in response.tool_calls:
        if tc["name"] == "multiply":
            result = multiply.invoke(tc["args"])
            print("Tool result:", result)

Output

Tool result: 21

Tool Calling with multiple tools

Here is another example, where we’ll see the workflow with multiple tool calls. Let’s say we have two different tools for addition and multiplication and tool call may contain both tools or any one of them. Workflow is as given below.

  1. LLM receives the user’s query.
  2. It decides whether the query needs external help (tool calling).
  3. If yes, appropriate ToolMessage is returned (tool call with name and args).
  4. The tool executes and returns a Result (e.g., live data, calculation, API response).
  5. That result is sent back to the LLM, which integrates it with the conversation.
  6. Finally, the LLM produces a polished, conversational answer for the user.

If you want to use a simple loop as shown in the previous example, then the code would be as given below.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_groq import ChatGroq
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file

# Multiplication tool
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

# Addition tool
@tool
def add(a: int, b: int) -> int:
    """Add two integers and return the result."""
    return a + b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

# Bind tools
llm_with_tools = llm.bind_tools([multiply, add])

# System prompt template
system_prompt = """
You are an AI assistant with access to external tools.
Your job is to decide when to call a tool, execute it correctly,
and then provide a polished, conversational answer to the user.

Available tools:
- multiply(a: int, b: int): returns the product of two integers
- add(a: int, b: int): returns the sum of two integers

Guidelines:
1. Think step by step about whether a tool is needed.
2. When calling a tool, output a structured tool call in JSON.
3. If no tool is needed, answer directly
4. Do not expose internal reasoning or chain-of-thought.
5. Only output either a tool call or a final user-friendly answer.
"""

# Build prompt template
prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("user", "{input}")
])

def multi_tool_feedback(query: str):
    # First LLM call: decide tool usage
    first_response = llm_with_tools.invoke(query)
    print("First response:", first_response)
    results = []
    if first_response.tool_calls:
        print("Tool calls:", first_response.tool_calls)
        for tool_call in first_response.tool_calls:
            tool_name = tool_call["name"]
            args = tool_call["args"]

            # Execute each tool
            if tool_name == "multiply":
                result = multiply.invoke(args)
            elif tool_name == "add":
                result = add.invoke(args)
            else:
                result = "Unknown tool"

            results.append(result)

        print("Tool results:", results)
        # Second LLM call: feed all results back
        final_answer = llm.invoke(
            f"User asked: {query}. The tools returned: {results}. "
            "Please combine these into a natural language answer."
        )
        return final_answer
    else:
        # No tool calls, just return the model’s text
        return first_response

response = multi_tool_feedback("Please multiply 5 and 3, then add 10 to the result.")
print("Final response:", response)  
print("Final Answer:", response.content)

Output

First response: content='' additional_kwargs={'tool_calls': [{'id': '1e8aj5waj', 'function': {'arguments': '{"a":5,"b":3}', 'name': 'multiply'}, 'type': 'function'}, 
{'id': 'dbpynva42', 'function': {'arguments': '{"a":15,"b":10}', 'name': 'add'}, 'type': 'function'}]} 
response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 295, 'total_tokens': 331, 'completion_time': 0.076297873, 'completion_tokens_details': None, 'prompt_time': 0.014930285, 'prompt_tokens_details': None, 'queue_time': 0.157809024, 'total_time': 0.091228158}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_3272ea2d91', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df1cb-134e-7232-b741-b9e941c64389-0'
tool_calls=[{'name': 'multiply', 'args': {'a': 5, 'b': 3}, 'id': '1e8aj5waj', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 15, 'b': 10}, 'id': 'dbpynva42', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 295, 'output_tokens': 36, 'total_tokens': 331}
Tool calls: [{'name': 'multiply', 'args': {'a': 5, 'b': 3}, 'id': '1e8aj5waj', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 15, 'b': 10}, 'id': 'dbpynva42', 'type': 'tool_call'}]
Tool results: [15, 25]
Final response: content='To solve the problem, we first multiply 5 and 3, which equals 15. Then, we add 10 to the result, giving us a final answer of 25.' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 39, 'prompt_tokens': 73, 'total_tokens': 112, 'completion_time': 0.124071334, 'completion_tokens_details': None, 'prompt_time': 0.006693922, 'prompt_tokens_details': None, 'queue_time': 0.063180878, 'total_time': 0.130765256}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_dae98b5ecb', 'service_tier': 'on_demand', 'finish_reason': 'stop', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df1cb-1507-7a33-aa60-6a5bd288b607-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 73, 'output_tokens': 39, 'total_tokens': 112}
Final Answer: To solve the problem, we first multiply 5 and 3, which equals 15. Then, we add 10 to the result, giving us a final answer of 25.

Points to Note

  • The system message is made more descriptive to give clarity to the LLM about the tools and their arguments.
  • While executing the tools, their results are appended to a list.
  • That list and the original query is sent again to the LLM to get a final, polished answer.

Multi- tool calling with chain.

Same example can be done in a more professional way by creating a chain and using RunnableLambda to execute functions.

from langchain_groq import ChatGroq
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_core.runnables import RunnableLambda
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file
# Define tools
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

@tool
def add(a: int, b: int) -> int:
    """Add two integers and return the result."""
    return a + b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

# Bind tools
llm_with_tools = llm.bind_tools([multiply, add])

# 4. System prompt template
system_prompt = """
You are an AI assistant with access to external tools.
Your job is to decide when to call a tool, execute it correctly,
and then provide a polished, conversational answer to the user.

Available tools:
- multiply(a: int, b: int): returns the product of two integers
- add(a: int, b: int): returns the sum of two integers

Guidelines:
1. Think step by step about whether a tool is needed.
2. When calling a tool, output a structured tool call in JSON.
3. If no tool is needed, answer directly
4. Do not expose internal reasoning or chain-of-thought.
5. Only output either a tool call or a final user-friendly answer.
"""

# Build prompt template
prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("user", "{input}")
])

# Define tool executor
def execute_tools(output):
    print("LLM output after first call:", output)
    results = []
    if output.tool_calls:
        for tc in output.tool_calls:
            if tc["name"] == "add":
                results.append(add.invoke(tc["args"]))
            elif tc["name"] == "multiply":
                results.append(multiply.invoke(tc["args"]))
            
    return {"tool_results": results, "query": output.content}

# Feed results back into LLM
def final_answer(data):
    return llm.invoke(
        f"User asked: {data['query']}. Tools returned: {data['tool_results']}. "
        "Please combine these into a natural language answer."
    )

# Chain
chain = (prompt 
        | llm_with_tools 
        | RunnableLambda(execute_tools) 
        | RunnableLambda(final_answer)
)

response = chain.invoke({"input": "Please add 5 and 3, then multiply the result by 2."})
print("Final response:", response)  
print("Final Answer:", response.content)

Output

LLM output after first call: content='' additional_kwargs={'tool_calls': [{'id': 'vz71y3drr', 'function': {'arguments': '{"a":5,"b":3}', 'name': 'add'}, 'type': 'function'}, {'id': '14hvbadej', 'function': {'arguments': '{"a":8,"b":2}', 'name': 'multiply'}, 'type': 'function'}]} 
response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 441, 'total_tokens': 477, 'completion_time': 0.092313015, 'completion_tokens_details': None, 'prompt_time': 0.022607, 'prompt_tokens_details': None, 'queue_time': 0.04985095, 'total_time': 0.114920015}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_dae98b5ecb', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df260-48af-7080-9360-37fc5caad39c-0'
tool_calls=[{'name': 'add', 'args': {'a': 5, 'b': 3}, 'id': 'vz71y3drr', 'type': 'tool_call'}, {'name': 'multiply', 'args': {'a': 8, 'b': 2}, 'id': '14hvbadej', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 441, 'output_tokens': 36, 'total_tokens': 477}
Final response: content='The tools returned two values: 8 and 16.' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 13, 'prompt_tokens': 57, 'total_tokens': 70, 'completion_time': 0.034086635, 'completion_tokens_details': None, 'prompt_time': 0.003220668, 'prompt_tokens_details': None, 'queue_time': 0.161129671, 'total_time': 0.037307303}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_3272ea2d91', 'service_tier': 'on_demand', 'finish_reason': 'stop', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df260-4b3a-73d2-b4ce-79a0fae5297a-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 57, 'output_tokens': 13, 'total_tokens': 70}
Final Answer: The tools returned two values: 8 and 16.

InjectedToolArg in Langchain tools

When building tools in LangChain, not every argument should come from the LLM. Some values, like session IDs, user tokens, timestamps, or database handles, are sensitive or system specific and must be injected at runtime. That’s exactly what InjectedToolArg in LangChain is designed for.

InjectedToolArg is a LangChain Core annotation that marks tool arguments as injected at runtime rather than generated by the LLM.

Arguments marked with InjectedToolArg are excluded from the schema sent to the model. The LLM never sees or generates them. That prevents the LLM from guessing or hallucinating values that should come from the environment (e.g., conversation IDs, request metadata, API keys).

Arguments marked with InjectedToolArg are provided by the developer, agent executor, or system context during tool execution.

InjectedToolArg Langchain example

Here is a simple example where Customer ID argument is marked as a InjectedToolArg, which means LLM won’t see it and even won’t try to guess it on its own. It has to be injected at runtime.

from typing import Annotated
from langchain_core.tools import tool, InjectedToolArg
from langchain_google_genai import ChatGoogleGenerativeAI
from dotenv import load_dotenv

load_dotenv()

# Define a tool with one normal arg (LLM-supplied) and one 
# injected arg (runtime-supplied)
@tool
def escalate_to_human(
    reason: str,
    customer_id: Annotated[int, InjectedToolArg]
) -> str:
    """Escalate the conversation to a human operator."""
    return f"Escalating: {reason} (Customer ID: {customer_id})"

# Create the LLM
llm = ChatGoogleGenerativeAI(model="gemini-3.1-flash-lite-preview", temperature=0.2)

# Bind the tool to the LLM
llm_with_tools = llm.bind_tools([escalate_to_human])

query = "The customer is very upset, escalate this."

# LLM processes the query and emits a tool call
response = llm_with_tools.invoke(query)

print("LLM raw response:", response)

if response.tool_calls:
    for tc in response.tool_calls:
        # Inject runtime context (customer_id from your system/session)
        # hardcodinhg customer_id=12345 for demo purposes
        runtime_context = {"customer_id": 12345}
        merged_args = {**tc["args"], **runtime_context}
        result = escalate_to_human.invoke(merged_args)
        print("Tool execution result:", result)
        # Feed the tool result back into the LLM to get final response
        response = llm.invoke(
            f"""
            Greet the customer using Customer ID (get from context) and inquire about the issue. 
            Context: {result}
            """
        )

print("Final LLM Response:", response)

print("Final LLM answer:", response.content[0].get("text"))

Output

LLM raw response: content=[] additional_kwargs={'function_call': {'name': 'escalate_to_human', 'arguments': '{"reason": "The customer is very upset and has requested an escalation."}'}, '__gemini_function_call_thought_signatures__': {'312283e6-b7ba-4149-97dd-1c93e5e97d8a': 'EjQKMgEMOdbHi4YjgRE760fr4+mtE0xB7dY5NZ3b3Dw6aZy70QE9eyXU9eDGgGKvMN8yu5z2'}} response_metadata={'finish_reason': 'STOP', 'model_name': 'gemini-3.1-flash-lite-preview', 'safety_ratings': [], 'model_provider': 'google_genai'} id='lc_run--019df2d2-81f9-75f3-9a97-f44e2ab3e660-0' 
tool_calls=[{'name': 'escalate_to_human', 'args': {'reason': 'The customer is very upset and has requested an escalation.'}, 'id': '312283e6-b7ba-4149-97dd-1c93e5e97d8a', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 59, 'output_tokens': 29, 'total_tokens': 88, 'input_token_details': {'cache_read': 0}}
Tool execution result: Escalating: The customer is very upset and has requested an escalation. (Customer ID: 12345)
Final LLM Response: content=[{'type': 'text', 'text': 'Hello, Customer 12345. I understand that you are very upset and have requested an escalation. I am here to assist you—could you please tell me more about the issue you are experiencing so I can address your concerns immediately?', 'extras': {'signature': 'EjQKMgEMOdbHAXp2AIc7nJffkQs5eevUxSKQmBMqsO/cAn089Zu+2eOdbvLIF6uunch0lbwA'}}] additional_kwargs={} response_metadata={'finish_reason': 'STOP', 'model_name': 'gemini-3.1-flash-lite-preview', 'safety_ratings': [], 'model_provider': 'google_genai'} id='lc_run--019df2d2-870d-71d3-9b4b-56ad10346d96-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 52, 'output_tokens': 50, 'total_tokens': 102, 'input_token_details': {'cache_read': 0}}
Final LLM answer: Hello, Customer 12345. I understand that you are very upset and have requested an escalation. I am here to assist you—could you please tell me more about the issue you are experiencing so I can address your concerns immediately?

That’s all for this topic Tool Calling in LangChain. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Citation Aware RAG Application in LangChain
  2. LangChain Conversational RAG with Multi-user Sessions
  3. RunnableBranch in LangChain With Examples
  4. Structured Output In LangChain
  5. Messages in LangChain

You may also like-

  1. Chatbot With Chat History - LangChain MessagesPlaceHolder
  2. Document Loaders in LangChain With Examples
  3. Array in Java With Examples
  4. Java CyclicBarrier With Examples
  5. Python assert Statement
  6. Multiple Inheritance in Python
  7. Spring Boot REST API Documentation - OpenAPI, Swagger
  8. Bean Scopes in Spring With Examples

Saturday, May 2, 2026

Equality And Relational Operators in Java

In Java, the equality and relational operators are used to compare values and determine the relationship between operands. These operators determine whether one value is greater than, less than, equal to, or not equal to another.

The equality and relational operators generate a boolean result; true if the condition holds, and false if it does not. This makes Equality And Relational Operators in Java essential for decision‑making and control flow in programs.

List of Equality and Relational operators

Operator Description
==equal to
!=not equal to
>greater than
>=greater than or equal to
<less than
<=less than or equal to

Important Note- You must use "==" when testing two primitive values for equality. Do not confuse it with the assignment operator "=", which assigns a value to a variable.

Example:
int a = 5, b = 5;
System.out.println(a == b); // true
System.out.println(a = b);  // assigns b to a, doesn't compare

Refer post Difference between equals() method and equality operator == in Java to know when to use equals method and when to use == operator.

Java equality and relational operators example

Following example shows the Java equality and relational operators in action.

public class RelationalDemo {
  public static void main(String[] args) {
    int a = 7;
    int b = 5;
    int c = 7;
    
    // This should get printed
    if(a == c){
      System.out.println("Values of a and c are equal");
    }
        
    // This won't be printed
    if(a == b){
      System.out.println("Values of a and b are equal");
    }
    
    if(a != b){
      System.out.println("Values of and b are not equal");
    }
    
    if(a > b){
      System.out.println("a is greater than b");
    }
        
    if(a >= c){
      System.out.println("a is greater than or equal to c");
    }
    
    // This won't be printed
    if(a < b){
      System.out.println("a is less than b");
    }
    
    if(b < a){
      System.out.println("b is less than a");
    }
  }
}

Output

Values of a and c are equal
Values of and b are not equal
a is greater than b
a is greater than or equal to c
b is less than a

That's all for this topic Equality And Relational Operators in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Basics Tutorial Page


Related Topics

  1. Arithmetic And Unary Operators in Java
  2. Conditional Operators in Java
  3. Array in Java
  4. String in Java Tutorial
  5. this Keyword in Java With Examples

You may also like-

  1. What are JVM, JRE and JDK in Java
  2. Java Automatic Numeric Type Promotion
  3. Why main Method static in Java
  4. Java Pass by Value or Pass by Reference
  5. Access Modifiers in Java - Public, Private, Protected and Default
  6. ArrayList in Java With Examples
  7. ConcurrentHashMap in Java With Examples
  8. Lambda Expressions in Java 8

throw Statement in Java Exception Handling

It is possible for a Java program to explicitly raise an exception, that is done using the throw statement in Java.

When the throw statement is executed inside a method, the program’s flow of execution halts immediately; any code written after the throw statement will not run.

General Syntax of throw Statement

General form of throw statement is-

throw throwableObject;
Here, throwableObject must be an instance of Throwable class or one of its subclasses.

Ways to Obtain a Throwable Object

We can get this throwableObject in 2 ways-

  • By using the Exception parameter in a catch block- re‑throwing a caught exception.
  • Create a new exception object using the new operator. For example
    throw new IllegalArgumentException("Invalid input provided");
    

Example Program Using throw Statement in Java

public class ThrowDemo {

 public static void main(String[] args) {
  ThrowDemo throwDemo = new ThrowDemo();
  try{
    throwDemo.displayValue();
  }catch(NullPointerException nExp){
     System.out.println("Exception caught in catch block of main");
     nExp.printStackTrace();;
  }
 }
 
 public void displayValue(){
  try{
    // Explicitly throwing a new exception object
    throw new NullPointerException();   
  }catch(NullPointerException nExp){
     System.out.println("Exception caught in catch block of displayValue");
     // Re‑throwing the caught exception
     throw nExp;
  }
 }
}

Note that in this program throw keyword is used at two places. First time, in the try block of displayValue() method, it uses the new operator to create an instance of type throwable.

Second time it uses the parameter of the catch block.

throw statement helps in preserving loose coupling

One of the best practices for the exception handling is to preserve loose coupling between layers of an application. This means that an implementation specific checked exception should not propagate to another layer.

As Example SQL exception from the DataAccessCode (DAO layer) should not propagate to the service (Business) layer. The common approach in that case is to wrap or convert database specific SQLException into an unchecked exception and then throw it.

catch(SQLException ex){
    throw new RuntimeException("Database error occurred", ex);
}

This ensures that the service layer deals with generalized exceptions rather than being tightly coupled to database‑specific details, making the application more maintainable and flexible.

That's all for this topic throw Statement in Java Exception Handling. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Difference Between throw And throws in Java
  2. Exception Propagation in Java Exception Handling
  3. Try-With-Resources in Java With Examples
  4. Creating Custom Exception Class in Java
  5. Java Exception Handling Interview Questions And Answers

You may also like-

  1. Fail-Fast Vs Fail-Safe Iterator in Java
  2. How HashMap Works Internally in Java
  3. finalize() Method in Java
  4. Java Pass by Value or Pass by Reference
  5. Why Class Name And File Name Should be Same in Java
  6. Java CyclicBarrier With Examples
  7. Lambda Expressions in Java 8
  8. Count Number of Times Each Character Appears in a String Java Program