Метод для типа массива и отдельного экземпляра [duplicate]

Это то, что я делаю, чтобы печатать различные версии в файле журнала. Я жестко закодировал расширенный путь, но приложения могут использовать servletContext.getRealPath("/") для чтения полного пути к папке webapp. Может печатать только предоставленные библиотеки или все, что угодно из папки lib.

// print library versions (jersey-common.jar, jackson-core-2.6.1.jar)
try {
    List<String> jars  = Arrays.asList( "jersey-common", "jackson-core", "openjpa", "mylib" );
    StringBuilder verbuf = new StringBuilder();
    for(File file : new File("/opt/tomcat/webapps/myapp/WEB-INF/lib/").listFiles() ) {
        String name = file.getName();
        if (file.isDirectory() || !file.isFile() || !name.endsWith(".jar") ) continue;
        name = name.substring(0, name.length()-4);
        boolean found = jars.contains(name);
        if (!found) {
            int idx = name.lastIndexOf('-');
            if (idx>0)
                found = jars.contains( name.substring(0, idx) );
        }
        if (!found) continue;

        JarFile jarFile = new JarFile(file, false);
        try {
            String ver;
            Manifest mf = jarFile.getManifest();
            if (mf!=null) {
                ver = mf.getMainAttributes().getValue("Bundle-Version");
                if (ver==null || ver.isEmpty())
                    ver = mf.getMainAttributes().getValue("Implementation-Version");
            } else ver=null;
            if (verbuf.length()>0) verbuf.append(", ");
            verbuf.append(name + "=" + (ver!=null?ver:"") );                        
        } finally {
            jarFile.close();
        }
    }
    System.out.println( verbuf.toString() );
} catch(Exception ex) {
    ex.printStackTrace();
}
3
задан icza 10 May 2017 в 12:15
поделиться

3 ответа

Всякий раз, когда метод хочет изменить получателя, он должен быть указателем на значение; метод должен иметь приемник указателя.

Если у метода есть приемник без указателя, копия будет выполнена и передана при вызове этого метода. С этого момента, независимо от того, что вы делаете с получателем, вы можете изменить только копию, а не оригинал.

Итак, в вашем примере:

func (p Person) SetName(newName string)  {
    name := p.GetName();
    *name = newName
}

Когда SetName(), копия сделана из Person. Внутри SetName() вы получите адрес поля name копии, которую вы изменяете. (На самом деле, копия копии, подробнее об этом позже ...)

Решение: используйте приемник-указатель:

func (p *Person) SetName(newName string)  {
    name := p.GetName();
    *name = newName
}

Из этого point, только *Person реализует Individual, поэтому используйте указатель при добавлении:

individuals = append(individuals, &person)

Это сложно, потому что после этого он не будет работать. Почему это?

Это потому, что у Person.GetName() все еще есть приемник без указателя:

func (p Person) GetName() *string {
    return &p.name
}

Поэтому, когда GetName() вызывается из SetName(), копия , и GetName() вернет адрес поля name копии, а SetName() изменит только копию, созданную для вызова GetName().

Итак, чтобы сделать все вы также должны использовать приемник указателей для GetName():

func (p *Person) GetName() *string {
    return &p.name
}

И теперь он работает, выводит (попробуйте на Go Playground ):

{Steve}
&{Peter}
{Peter}

Но знайте, что самый простой и рекомендуемый способ - просто:

func (p *Person) SetName(newName string) {
    p.name = newName
}

Это все, что нужно.

4
ответ дан icza 15 August 2018 в 16:45
поделиться
  • 1
    Я полагаю, что для GetName было бы более идиоматично возвращать значение string? – Chris Drew 10 May 2017 в 11:26
  • 2
    Спасибо за хороший ответ! Если я несколько раз назову p1 := individuals[0], а затем напечатаю адрес с fmt.Println(&p1) после каждого вызова, я получаю разные адреса каждый раз. p2 := individuals[0], а затем fmt.Println(&p2) напечатает другой адрес. Зачем? – Rox 10 May 2017 в 11:36
  • 3
    @ChrisDrew Да, очевидно. Я просто хотел узнать из кода то, что представил искатель, потому что я думаю, что он назидает / освещает (что копия копии не является самоочевидной). – icza 10 May 2017 в 11:36
  • 4
    @Rox p1 := individuals[0] является short variable declaration , который создает локальную переменную, а &p1 является адресом локальной переменной (а не значением указателя, если она содержат указатель), который будет (может) отличаться каждый раз. – icza 10 May 2017 в 11:38

См. https://play.golang.org/p/eg8oYDV6Xx p1 и p2 уже являются адресами, вам не нужно их адрес (адрес адреса), просто распечатайте p1 и p2 - они будут такими же, как вы можете видеть.

0
ответ дан Ravi 15 August 2018 в 16:45
поделиться

Две вещи, чтобы исправить это:

  • Вам нужно, чтобы методы прикреплялись с помощью метода «указатель», иначе имя изменяется только внутри метода.
  • Вам нужно использовать указатели для фактических переменных Person, так как им необходимо реализовать интерфейс
type Individual interface {
    GetName() *string
    SetName(name string)
}

type Person struct {
    name string
}

// Implement functions of the Individual interface
func (p Person) GetName() *string  {
    return &p.name
}

func (p *Person) SetName(newName string)  {
    p.name = newName
}


var individuals []Individual

func main() {
    person := &Person{name: "Steve"}
    fmt.Println(person)

    individuals = append(individuals, person) // append Person to slice
    p1 := individuals[0]     // Retrieve the only Person from slice
    p1.SetName("Peter")      // Change name
    fmt.Println(p1)          // Prints "Steve"
    fmt.Println(person)      // Prints "Steve"
}

Пример на Go Playground .

3
ответ дан TheHippo 15 August 2018 в 16:45
поделиться
  • 1
    Как бы то ни было, я думаю, что GetName немного вводит в заблуждение. Он возвращает указатель на string в копии Person. Я предполагаю, что метод должен либо иметь приемник указателя, либо возвращать значение, нет? – Chris Drew 10 May 2017 в 11:28
Другие вопросы по тегам:

Похожие вопросы: