Java: Alt paket görünürlüğü mü?


150

Projemde iki paket var: odp.projve odp.proj.test. Sadece bu iki paketteki sınıflar tarafından görülmesini istediğim belirli yöntemler var. Bunu nasıl yapabilirim?

EDIT: Java bir alt paket kavramı yoksa, bunun herhangi bir yolu var mı? Yalnızca test kullanıcıları ve bu paketin diğer üyeleri tarafından kullanılabilir olmasını istediğim belirli yöntemler var. Her şeyi aynı pakete atmalı mıyım? Kapsamlı yansıma mı kullanıyorsunuz?




2
Bir kenara, testler sadece hiç test etmelidir davranışı paketinden dışından gözlemlenebilir olarak nesnelerin. Paket kapsamı yöntemlerine / sınıflarına testlerinizden erişmek, testlerin muhtemelen davranışları değil uygulamaları test ettiğini söylüyor. Maven veya gradle gibi bir derleme aracı kullanarak, testlerinizin aynı sınıf yolunda çalışmasını kolaylaştıracak, ancak son kavanoza dahil edilmeyecek (iyi bir şey), bu nedenle farklı paket adlarına sahip olmaları gerekmez. Yine de bunları ayrı paketler halinde koymak, özel / varsayılan kapsama erişmemenizi ve böylece yalnızca genel API'yi test etmenizi sağlamaktır .
derekv

3
Bu tamamen davranış odaklı bir şekilde çalışıyorsanız ve testlerinizin yalnızca kara kutu testi yapmasını istiyorsanız bu doğru olabilir. Ancak istenen davranışın uygulanmasının kaçınılmaz olarak yüksek bir siklomatik karmaşıklık gerektirdiği durumlar olabilir. Bu durumda, uygulamayı daha küçük, daha basit parçalara ayırmak (yine de uygulamaya özeldir) ve bu parçalar aracılığıyla farklı yollarda beyaz kutu testi yapmak için bazı birim testleri yazmak güzel olabilir.
James Woods

Yanıtlar:


165

Yapamazsın. Java'da orada alt paketin hiçbir kavramdır, yani odp.projve odp.proj.testtamamen ayrı paketlerdir.


10
Bu şekilde sevmeme rağmen, çoğu IDE'nin aynı ada sahip paketleri bir araya getirmesi kafa karıştırıcı. Açıklama için teşekkürler.
JacksOnF1re

Bu kesinlikle doğru değil: JLS , alt paketleri tanımlasa da, sahip oldukları tek dil önemi "üst düzey tiple aynı basit ada sahip bir alt pakete sahip bir pakete karşı" yasaklamaktır. Bu soruya, bunu ayrıntılı olarak açıklayan bir cevap ekledim.
M. Justin

59

Paketlerinizin adları, buradaki uygulamanın birim sınama için olduğunu gösterir. Kullanılan tipik desen, test etmek istediğiniz sınıfları ve birim test kodunu aynı pakete (sizin durumunuzda odp.proj) ancak farklı kaynak ağaçlarına koymaktır . Böylece derslerinizi src/odp/projve test kodunuzu girersiniz test/odp/proj.

Java hiçbiri belirtilmediğinde varsayılan erişim değiştiricisi olan "paket" erişim değiştiricisine sahiptir (yani, genel, özel veya korumalı belirtmezsiniz). "Paket" erişim değiştiricisiyle, yalnızca içindeki sınıflar odp.projyöntemlere erişebilir. Ancak Java'da, erişim kurallarını uygulamak için erişim değiştiricilerine güvenilemeyeceğini unutmayın, çünkü yansıma ile herhangi bir erişim mümkündür. Erişim değiştiriciler yalnızca önericidir (kısıtlayıcı bir güvenlik yöneticisi yoksa).


11

Bu arasında hiçbir özel ilişki olduğunu odp.projveodp.proj.test - görünüşe göre ilişkili olarak adlandırılırlar.

Odp.proj.test paketi sadece testler sağlıyorsa aynı paket adını ( odp.proj) kullanabilirsiniz. Eclipse ve Netbeans gibi IDE'ler ayrı klasörler ( src/main/java/odp/projvesrc/test/java/odp/proj , aynı paket adına sahip ancak JUnit anlambilimine sahip ) oluşturur.

Bu IDE'lerin içindeki yöntemler için testler üreteceğini odp.projve var olmadığı test yöntemleri için uygun klasörü oluşturacağını unutmayın.


5

Bunu IntelliJ'de yaptığımda, kaynak ağacım şöyle görünüyor:

src         // source root
- odp
   - proj   // .java source here
- test      // test root
  - odp
     - proj // JUnit or TestNG source here

4

EDIT: Java bir alt paket kavramı yoksa, bunun herhangi bir yolu var mı? Yalnızca test kullanıcıları ve bu paketin diğer üyeleri tarafından kullanılabilecek belirli yöntemlerim var.

Muhtemelen onları göstermemek için güdülerinize bağlıdır, ancak tek neden, kamusal arayüzü sadece test için (veya başka bir iç şey) amaçlanan şeylerle kirletmek istemiyorsanız, yöntemleri bir ayrı genel arayüzü ve "gizli" yöntemleri tüketicilerin bu arayüzü kullanmak var. Başkalarının arayüzü kullanmasını engellemeyecek, ancak neden yapmanız gerektiğini göremiyorum.

Birim testleri için ve lotu yeniden yazmadan mümkünse, aynı paketi kullanma önerilerini izleyin.


3

Diğerlerinin açıkladığı gibi, Java'da bir "alt paket" diye bir şey yoktur: tüm paketler izole edilir ve ebeveynlerinden hiçbir şey miras alınmaz.

Korumalı sınıf üyelerine başka bir paketten erişmenin kolay bir yolu, sınıfı genişletmek ve üyeleri geçersiz kılmaktır.

Örneğin, ClassInApakette erişmek için a.b:

package a;

public class ClassInA{
    private final String data;

    public ClassInA(String data){ this.data = data; }

    public String getData(){ return data; }

    protected byte[] getDataAsBytes(){ return data.getBytes(); }

    protected char[] getDataAsChars(){ return data.toCharArray(); }
}

bu pakette, ihtiyacınız olan yöntemleri geçersiz kılan bir sınıf oluşturun ClassInA:

package a.b;

import a.ClassInA;

public class ClassInAInB extends ClassInA{
    ClassInAInB(String data){ super(data); }

    @Override
    protected byte[] getDataAsBytes(){ return super.getDataAsBytes(); }
}

Bu, diğer paketteki sınıf yerine geçersiz kılma sınıfını kullanmanızı sağlar:

package a.b;

import java.util.Arrays;

import a.ClassInA;

public class Driver{
    public static void main(String[] args){
        ClassInA classInA = new ClassInA("string");
        System.out.println(classInA.getData());
        // Will fail: getDataAsBytes() has protected access in a.ClassInA
        System.out.println(Arrays.toString(classInA.getDataAsBytes()));

        ClassInAInB classInAInB = new ClassInAInB("string");
        System.out.println(classInAInB.getData());
        // Works: getDataAsBytes() is now accessible
        System.out.println(Arrays.toString(classInAInB.getDataAsBytes()));
    }
}

Bunun yalnızca genişletme sınıflarına (kalıtım) görebilen korumalı üyeler için geçerli olduğunu ve yalnızca aynı paket içindeki alt / genişletme sınıflarına görünen pakete özel üyeler için geçerli olmadığını unutmayın. Umarım bu birine yardımcı olur!


3

Buradaki cevapların çoğu, Java'da bir alt paket diye bir şey olmadığını belirtti, ancak bu kesinlikle doğru değil. Bu terim Java Dil Spesifikasyonunda Java 6'ya kadar ve muhtemelen daha da geriye gitmiştir (Java'nın önceki sürümleri için JLS'nin serbestçe erişilebilir bir sürümü yoktur). Alt paketlerin etrafındaki dil, Java 6'dan bu yana JLS'de çok fazla değişmedi.

Java 13 JLS :

Bir paketin üyeleri, alt paketleri ve paketin tüm derleme birimlerinde bildirilen tüm üst düzey sınıf türleri ve üst düzey arabirim türleridir.

Örneğin, Java SE Platform API'sında:

  • Paket javaalt paketlerin vardır awt, applet, io, lang, net, ve util, ancak hiçbir derleme birimleri.
  • Paket java.awtadında bir alt paketin imageyanı sıra sınıf ve arabirim türlerinin bildirimlerini içeren bir dizi derleme birimine sahiptir.

Paketler ve sınıflar / arayüzler arasındaki adlandırma kısıtlamalarını zorladığı gibi, alt paket kavramı da geçerlidir:

Bir pakette aynı ada sahip iki üye olmayabilir veya derleme zamanı hatası oluşur.

İşte bazı örnekler:

  • Paket java.awtbir alt pakete sahip olduğundan, adlı imagebir sınıf veya arabirim türü bildirimi içeremez (ve içermez) image.
  • Bu pakette adlandırılmış bir paket mouseve üye türü varsa Button(o zaman bu ad olarak adlandırılabilir mouse.Button), tam olarak nitelenmiş mouse.Buttonveya mouse.Button.Click.
  • Eğer com.nighthacks.java.jagbir tür tam adıdır, o zaman kimin tam ad olduğunu herhangi bir paket olamaz com.nighthacks.java.jagya com.nighthacks.java.jag.scrabble.

Ancak bu adlandırma sınırlamadır tek dile göre alt paket sağlanan önem:

Paketler için hiyerarşik adlandırma yapısı, ilgili paketleri geleneksel bir şekilde organize etmek için uygun olmakla birlikte, bu pakette beyan edilen üst düzey tiple aynı basit ada sahip bir alt pakete sahip bir pakete karşı yasağın dışında hiçbir önemi yoktur. .

Örneğin, adlandırılmış bir paket ile adlandırılmış oliverbaşka bir paket oliver.twistarasında veya evelyn.woodve adlı paketler arasında özel bir erişim ilişkisi yoktur evelyn.waugh. Diğer bir deyişle, adlı oliver.twistpaketteki oliverkodun, paket içinde bildirilen türlere, diğer paketlerdeki koddan daha iyi erişimi yoktur .


Bu bağlamda, sorunun kendisine cevap verebiliriz. Bir paket ve onun alt paketi arasında veya bir üst paketin iki farklı alt paketi arasında açıkça özel bir erişim ilişkisi olmadığından, bir yöntemi istenen şekilde iki farklı pakete görünür hale getirmenin bir yolu yoktur. Bu belgelenmiş, kasıtlı bir tasarım kararıdır.

Yöntem herkese açık hale getirilebilir ve tüm paketler ( odp.projve dahil odp.proj.test) verilen yöntemlere erişebilir veya yöntem paketi özel hale getirebilir (varsayılan görünürlük) ve doğrudan erişmesi gereken tüm kodlar yöntemle aynı (alt) paket.

Bununla birlikte, Java'daki çok standart bir uygulama, test kodunu kaynak koduyla aynı pakete, ancak dosya sisteminde farklı bir yere koymaktır. Örneğin, içinde Maven inşa aracı, kongre bu kaynak ve test dosyaları koymak olacaktır src/main/java/odp/projve src/test/java/odp/projsırasıyla. Derleme aracı bunu derlediğinde, her iki dosya kümesi de odp.projpakette sonlanır , ancak srcüretim dosyasında yalnızca dosyalar bulunur; test dosyaları sadece üretim sırasında üretim dosyalarını doğrulamak için kullanılır. Bu kurulumla, test kodu, aynı pakette olacağı için test ettiği kodun özel veya korumalı koduna ait herhangi bir pakete serbestçe erişebilir.

Test / üretim durumu olmayan alt paketler veya kardeş paketleri arasında kod paylaşımı yapmak istediğinizde, bazı kütüphanelerin kullandığını gördüğüm bir çözüm, bu paylaşılan kodu genel olarak koymak, ancak dahili kütüphane için tasarlandığını belgelemek sadece kullan.


0

Erişim değiştiriciyi yöntemin önüne koymadan özel paket olduğunu söylersiniz.
Aşağıdaki örneğe bakın.

package odp.proj;
public class A
{
    void launchA() { }
}

package odp.proj.test;
public class B
{
    void launchB() { }
}

public class Test
{
    public void test()
    {
        A a = new A();
        a.launchA()    // cannot call launchA because it is not visible
    }
}

0

PackageVisibleHelper sınıfı ile ve PackageVisibleHelperFactory donmadan önce özel olarak saklayın, her yerde launchA (PackageVisibleHelper tarafından) yöntemini çağırabiliriz :)

package odp.proj;
public class A
 {
    void launchA() { }
}

public class PackageVisibleHelper {

    private final PackageVisibleHelperFactory factory;

    public PackageVisibleHelper(PackageVisibleHelperFactory factory) {
        super();
        this.factory = factory;
    }

    public void launchA(A a) {
        if (factory == PackageVisibleHelperFactory.INSTNACNE && !factory.isSampleHelper(this)) {
            throw new IllegalAccessError("wrong PackageVisibleHelper ");
        }
        a.launchA();
    }
}


public class PackageVisibleHelperFactory {

    public static final PackageVisibleHelperFactory INSTNACNE = new PackageVisibleHelperFactory();

    private static final PackageVisibleHelper HELPER = new PackageVisibleHelper(INSTNACNE);

    private PackageVisibleHelperFactory() {
        super();
    }

    private boolean frozened;

    public PackageVisibleHelper getHelperBeforeFrozen() {
        if (frozened) {
            throw new IllegalAccessError("please invoke before frozen!");
        }
        return HELPER;
    }

    public void frozen() {
        frozened = true;
    }

    public boolean isSampleHelper(PackageVisibleHelper helper) {
        return HELPER.equals(helper);
    }
}
package odp.proj.test;

import odp.proj.A;
import odp.proj.PackageVisibleHelper;
import odp.proj.PackageVisibleHelperFactory;

public class Test {

    public static void main(String[] args) {

        final PackageVisibleHelper helper = PackageVisibleHelperFactory.INSTNACNE.getHelperBeforeFrozen();
        PackageVisibleHelperFactory.INSTNACNE.frozen();


        A a = new A();
        helper.launchA(a);

        // illegal access       
        new PackageVisibleHelper(PackageVisibleHelperFactory.INSTNACNE).launchA(a); 
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.