Interface एक कॉन्ट्रैक्ट है जो अमूर्त विधियों के एक सेट को परिभाषित करता है जिन्हें क्लास को लागू करना होता है। Java और Kotlin में, इंटरफ़ेस एब्स्ट्रैक्शन और पॉलिमॉर्फिज़म की प्राथमिक प्रणाली हैं। Java 8+ में, इंटरफ़ेस default और static विधियाँ रख सकते हैं, Kotlin में — डिफ़ॉल्ट इम्प्लीमेंटेशन। Google Android Developers (2025) के अनुसार, 90% Android प्रोजेक्ट्स में इंटरफ़ेस का उपयोग आर्किटेक्चर लेयर — रिपॉज़िटरी, UseCase और सेवाओं को परिभाषित करने के लिए किया जाता है।
मुख्य बिंदु
Interface एक संदर्भ प्रकार है जिसमें अमूर्त विधियाँ, स्थिरांक और डिफ़ॉल्ट विधियाँ होती हैं। इंटरफ़ेस को लागू करने वाली क्लास को उसकी सभी अमूर्त विधियों का इम्प्लीमेंटेशन प्रदान करना होता है। Java में, इंटरफ़ेस में स्थिति (इंस्टेंस फ़ील्ड) नहीं हो सकती; Kotlin भी इस प्रतिबंध का पालन करता है लेकिन एक्सेसर्स के साथ प्रॉपर्टी का समर्थन करता है।
public interface Repository {
T findById(Long id);
List findAll();
T save(T entity);
void deleteById(Long id);
default long count() {
return findAll().size();
}
}
@Entity
public class UserEntity {
private Long id;
private String email;
// गेटर्स और सेटर्स
}
public class UserRepositoryImpl implements Repository {
private final EntityManager em;
public UserEntity findById(Long id) {
return em.find(UserEntity.class, id);
}
public List findAll() {
return em.createQuery("FROM UserEntity", UserEntity.class)
.getResultList();
}
// अन्य विधियाँ
}Repository<T> CRUD विधियों वाला एक जेनेरिक इंटरफ़ेस है। डिफ़ॉल्ट विधि count() एक डिफ़ॉल्ट इम्प्लीमेंटेशन प्रदान करती है जिसे ओवरराइड किया जा सकता है। UserRepositoryImpl डेटा एक्सेस के लिए EntityManager का उपयोग करके इंटरफ़ेस को लागू करता है। यह दृष्टिकोण वास्तविक डेटाबेस के बिना इंटरफ़ेस mock के माध्यम से डेटा लेयर का परीक्षण करने की अनुमति देता है।
इंटरफ़ेस और अमूर्त क्लास के बीच चुनाव साझा स्थिति की उपस्थिति और प्रकारों के बीच संबंधों पर निर्भर करता है। इंटरफ़ेस एक कॉन्ट्रैक्ट (क्लास क्या कर सकता है) परिभाषित करता है, अमूर्त क्लास एक सामान्य इम्प्लीमेंटेशन (क्लास क्या है) परिभाषित करता है।
| मापदंड | Interface | Abstract Class |
|---|---|---|
| स्थिति (फ़ील्ड) | केवल static final स्थिरांक | हाँ, कोई भी फ़ील्ड |
| कंस्ट्रक्टर | नहीं | हाँ |
| एकाधिक वंशानुक्रम | हाँ (implements) | नहीं (extends एक) |
| एक्सेस मॉडिफायर | public (Java 8-), default विधियाँ | सभी (private, protected, public) |
| कब उपयोग करें | असंबंधित क्लासों के लिए कॉन्ट्रैक्ट | संबंधित क्लासों के लिए सामान्य आधार |
Clean Architecture में, इंटरफ़ेस आंतरिक लेयर (domain) में रखे जाते हैं, और इम्प्लीमेंटेशन बाहरी लेयर (data) में। यह Dependency Rule बनाए रखने की अनुमति देता है: बाहरी लेयर आंतरिक पर निर्भर करती है, लेकिन इसके विपरीत नहीं। अमूर्त क्लास का उपयोग अक्सर टेम्पलेट विधियों (Template Method pattern) के लिए किया जाता है।
Kotlin इंटरफ़ेस Java की तुलना में अधिक लचीले हैं: वे अमूर्त प्रॉपर्टी घोषित कर सकते हैं और विधि इम्प्लीमेंटेशन प्रदान कर सकते हैं। Java के विपरीत, Kotlin by कीवर्ड के माध्यम से डेलिगेशन का समर्थन करता है, जो Delegate पैटर्न को लागू करते समय boilerplate को शक्तिशाली रूप से कम करता है।
interface ApiService {
val baseUrl: String // अमूर्त प्रॉपर्टी
suspend fun fetchData(): Result>
fun getEndpoint(path: String): String {
return "$baseUrl/$path" // डिफ़ॉल्ट कार्यान्वयन
}
}
class RetrofitApiService(
override val baseUrl: String
) : ApiService {
private val client = Retrofit.Builder()
.baseUrl(baseUrl)
.build()
override suspend fun fetchData(): Result>
{
// अनुरोध कार्यान्वयन
}
}
// by के माध्यम से डेलिगेशन
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) = println(message)
}
class UserService(logger: Logger) : Logger by loggerUserService डेलिगेशन (by) के माध्यम से Logger इंटरफ़ेस को लागू करता है। सभी log() कॉल रैपर विधियाँ लिखे बिना logger ऑब्जेक्ट को अग्रेषित की जाती हैं। यह कंपोज़िशन का एक उदाहरण है जिसके लिए Java में 5-10 लाइन boilerplate कोड की आवश्यकता होगी।
Clean Architecture Android के लिए एप्लिकेशन को स्पष्ट रूप से लेयर में अलग करती है। इंटरफ़ेस लेयरों के बीच सीमा के रूप में कार्य करते हैं: Domain रिपॉज़िटरी और UseCase इंटरफ़ेस परिभाषित करता है, Data इम्प्लीमेंटेशन प्रदान करता है। यह बिज़नेस लॉजिक बदले बिना इम्प्लीमेंटेशन बदलने की अनुमति देता है — Room से Firebase या REST से GraphQL में माइग्रेट करते समय एक मुख्य लाभ।
// Domain layer — इंटरफ़ेस (कॉन्ट्रैक्ट)
interface UserRepository {
suspend fun getUser(id: String): User
suspend fun updateUser(user: User)
}
// Domain layer — use case (इंटरफ़ेस पर निर्भर करता है)
class GetUserUseCase(
private val repository: UserRepository
) {
suspend operator fun invoke(id: String): Result {
return runCatching { repository.getUser(id) }
}
}
// Data layer — कार्यान्वयन
class UserRepositoryImpl(
private val api: UserApi,
private val dao: UserDao
) : UserRepository {
override suspend fun getUser(id: String): User {
val cached = dao.getUser(id)
if (cached != null) return cached
val remote = api.fetchUser(id)
dao.insertUser(remote)
return remote
}
// updateUser...
}UserRepository domain लेयर में एक इंटरफ़ेस है। GetUserUseCase इम्प्लीमेंटेशन पर नहीं, इंटरफ़ेस पर निर्भर करता है। data लेयर में UserRepositoryImpl API और स्थानीय डेटाबेस को मिलाकर इंटरफ़ेस को लागू करता है। यह आर्किटेक्चर डेटाबेस या नेटवर्क सेट किए बिना mock रिपॉज़िटरी के साथ UseCases का परीक्षण करने की अनुमति देता है।
Default विधियाँ Java 8 में पिछड़ी संगतता को तोड़े बिना इंटरफ़ेस के विकासवादी विस्तार के लिए जोड़ी गईं। यदि ArrayList Collection में जोड़ी गई नई stream() विधि को लागू नहीं करता, तो पुराना कोड काम करना जारी रखता। इंटरफ़ेस में static विधियाँ इंटरफ़ेस से संबंधित उपयोगिताओं के रूप में कार्य करती हैं — उपयोगिता क्लास का विकल्प।
public interface Vehicle {
void start();
void stop();
default void honk() {
System.out.println("Beep!");
}
static Vehicle of(String type) {
if ("car".equals(type)) return new Car();
return new Bicycle();
}
// स्थिरांक
String CATEGORY = "transport";
}Default विधियाँ diamond problem को हल करती हैं: यदि कोई क्लास समान default विधि वाले दो इंटरफ़ेस लागू करता है, तो कंपाइलर स्पष्ट ओवरराइडिंग की आवश्यकता रखता है। Static विधियाँ इंटरफ़ेस नाम के माध्यम से बुलाई जाती हैं — Vehicle.of("car"), बिना इंस्टेंस के।
इंटरफ़ेस डिज़ाइन में गलतियाँ नाजुक कोड, परीक्षण जटिलता और SOLID उल्लंघनों की ओर ले जाती हैं। आइए तीन सामान्य समस्याओं पर विचार करें।
15+ विधियों वाला इंटरफ़ेस Interface Segregation Principle (ISP) का उल्लंघन करता है। एक उदाहरण पुराना java.util.Dictionary है जिसमें 10+ विधियाँ हैं। समाधान: कई छोटे इंटरफ़ेस में विभाजित करें — ReadableRepository, WritableRepository, SearchableRepository। क्लाइंट (सेवा) केवल उन विधियों पर निर्भर करता है जिनकी उसे आवश्यकता है।
पॉलिमॉर्फिज़म की वास्तविक आवश्यकता के बिना हर क्लास के लिए इंटरफ़ेस बनाना Interface overkill एंटीपैटर्न है। संकेत: इंटरफ़ेस का ठीक एक इम्प्लीमेंटेशन है, और प्रोजेक्ट में विकल्प जोड़ने की कोई योजना नहीं है। समाधान: इंटरफ़ेस तभी जोड़ें जब दूसरा इम्प्लीमेंटेशन विकल्प दिखाई दे या परीक्षण के लिए mock की आवश्यकता हो।
अक्सर पूछे जाने वाले प्रश्न
Interface केवल एक कॉन्ट्रैक्ट (विधि हस्ताक्षर) परिभाषित करता है, इसमें स्थिति नहीं हो सकती और यह एकाधिक वंशानुक्रम का समर्थन करता है। Abstract class में फ़ील्ड, कंस्ट्रक्टर और लागू की गई विधियाँ हो सकती हैं, लेकिन एक क्लास केवल एक अमूर्त क्लास को इनहेरिट कर सकता है। Java 8 से, इंटरफ़ेस को default और static विधियाँ मिली हैं, जिससे अंतर कम हुआ है।
हाँ, Java और Kotlin में इंटरफ़ेस वंशानुक्रम का समर्थन करते हैं। public interface AdvancedRepository<T> extends Repository<T>, Pageable एक इंटरफ़ेस है जो दो अन्य को जोड़ता है। AdvancedRepository को लागू करने वाली क्लास को दोनों मूल इंटरफ़ेस की सभी विधियों को लागू करना होगा। एकाधिक वंशानुक्रम केवल इंटरफ़ेस के लिए अनुमत है।
फंक्शनल इंटरफ़ेस एक एकल अमूर्त विधि (SAM — Single Abstract Method) वाला इंटरफ़ेस है। @FunctionalInterface एनोटेशन इस प्रतिबंध की गारंटी देता है। उदाहरण: Runnable, Callable, Comparator, Consumer। फंक्शनल इंटरफ़ेस Java 8 लैम्ब्डा एक्सप्रेशन का आधार हैं: () -> System.out.println() Runnable को लागू करता है।
Default विधियाँ सभी लागू करने वाली क्लासों को बदले बिना इंटरफ़ेस में नई विधियाँ जोड़ने की अनुमति देती हैं। उदाहरण के लिए, Java 8 ने Collection में stream() को default विधि के रूप में जोड़ा। इस प्रणाली के बिना, foreach(), stream() और अन्य विधियों के लिए JDK में हज़ारों क्लासों में बदलाव की आवश्यकता होती। Default पिछड़ी-संगत विस्तार का एक तरीका है।
Kotlin क्लासों के एकाधिक वंशानुक्रम को प्रतिबंधित करता है लेकिन इंटरफ़ेस के एकाधिक इम्प्लीमेंटेशन की अनुमति देता है। यदि दो इंटरफ़ेस में समान हस्ताक्षर और डिफ़ॉल्ट इम्प्लीमेंटेशन वाली विधि है, तो कंपाइलर super<InterfaceName>.method() कॉल के साथ स्पष्ट ओवरराइडिंग की आवश्यकता रखता है। यह कंपाइलेशन स्तर पर diamond problem को हल करता है।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें